From 6335c8e942cc69a21266d53313f049665dd8e0e2 Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Mon, 6 Oct 2025 14:02:18 -0400 Subject: [PATCH] basic abstraction first commit --- .gitignore | 5 ++ .../.idea/.gitignore | 13 ++++ .../.idea/encodings.xml | 4 ++ .../.idea/indexLayout.xml | 8 +++ .../.idea.Svrnty.GeoManagement/.idea/vcs.xml | 6 ++ CLAUDE.md | 58 ++++++++++++++++++ .../Abstractions/IAddress.cs | 11 ++++ .../Abstractions/IGeoManagementProvider.cs | 19 ++++++ .../Models/Address.cs | 61 +++++++++++++++++++ .../Models/FormattedAddressType.cs | 8 +++ .../Svrnty.GeoManagement.Abstractions.csproj | 9 +++ .../Svrnty.GeoManagement.Tests.csproj | 23 +++++++ Svrnty.GeoManagement.sln | 22 +++++++ 13 files changed, 247 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.idea.Svrnty.GeoManagement/.idea/.gitignore create mode 100644 .idea/.idea.Svrnty.GeoManagement/.idea/encodings.xml create mode 100644 .idea/.idea.Svrnty.GeoManagement/.idea/indexLayout.xml create mode 100644 .idea/.idea.Svrnty.GeoManagement/.idea/vcs.xml create mode 100644 CLAUDE.md create mode 100644 Svrnty.GeoManagement.Abstractions/Abstractions/IAddress.cs create mode 100644 Svrnty.GeoManagement.Abstractions/Abstractions/IGeoManagementProvider.cs create mode 100644 Svrnty.GeoManagement.Abstractions/Models/Address.cs create mode 100644 Svrnty.GeoManagement.Abstractions/Models/FormattedAddressType.cs create mode 100644 Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj create mode 100644 Svrnty.GeoManagement.Tests/Svrnty.GeoManagement.Tests.csproj create mode 100644 Svrnty.GeoManagement.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.idea/.idea.Svrnty.GeoManagement/.idea/.gitignore b/.idea/.idea.Svrnty.GeoManagement/.idea/.gitignore new file mode 100644 index 0000000..e5789c9 --- /dev/null +++ b/.idea/.idea.Svrnty.GeoManagement/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/projectSettingsUpdater.xml +/contentModel.xml +/.idea.Svrnty.GeoManagement.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.Svrnty.GeoManagement/.idea/encodings.xml b/.idea/.idea.Svrnty.GeoManagement/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.Svrnty.GeoManagement/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.Svrnty.GeoManagement/.idea/indexLayout.xml b/.idea/.idea.Svrnty.GeoManagement/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.Svrnty.GeoManagement/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.Svrnty.GeoManagement/.idea/vcs.xml b/.idea/.idea.Svrnty.GeoManagement/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.Svrnty.GeoManagement/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..df1b73b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,58 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**Svrnty.GeoManagement** is a .NET 8 library providing abstractions for geocoding and address management services. The solution follows a provider pattern to allow different geocoding service implementations. + +## Solution Structure + +The solution contains two projects: + +- **Svrnty.GeoManagement.Abstractions**: Core abstractions and models for geocoding operations +- **Svrnty.GeoManagement.Tests**: xUnit test project + +## Key Architecture Patterns + +### Provider Pattern +The library uses the `IGeoManagementProvider` interface to abstract geocoding services. Implementations should provide: +- Forward geocoding (address to coordinates) +- Reverse geocoding (coordinates to address) +- Address normalization + +### Address Model +The `Address` record in `Svrnty.GeoManagement.Abstractions/Models/Address.cs` is the central data structure. It: +- Implements `IAddress` interface +- Includes optional `GeoPoint` location data +- Provides formatted address output via `GetFormattedAddress()` method with three format types (Full, Compact, MultiLine) +- Has an `IsNormalized` flag to track address validation status + +## Common Commands + +### Build +```bash +dotnet build +``` + +### Run Tests +```bash +dotnet test +``` + +### Run Specific Test +```bash +dotnet test --filter "FullyQualifiedName~TestNamespace.TestClass.TestMethod" +``` + +### Restore Dependencies +```bash +dotnet restore +``` + +## Development Notes + +- Target framework: .NET 8 +- Nullable reference types are enabled +- The Abstractions project is designed to be provider-agnostic - don't add provider-specific dependencies +- When implementing `IGeoManagementProvider`, all methods support `CancellationToken` for async operations diff --git a/Svrnty.GeoManagement.Abstractions/Abstractions/IAddress.cs b/Svrnty.GeoManagement.Abstractions/Abstractions/IAddress.cs new file mode 100644 index 0000000..b4f8718 --- /dev/null +++ b/Svrnty.GeoManagement.Abstractions/Abstractions/IAddress.cs @@ -0,0 +1,11 @@ +namespace Svrnty.GeoManagement.Abstractions.Abstractions; + +public interface IAddress +{ + public string Line1 { get; } + public string? Line2 { get; } + public string City { get; } + public string Subdivision { get; } + public string PostalCode { get; } + public string Country { get; } +} \ No newline at end of file diff --git a/Svrnty.GeoManagement.Abstractions/Abstractions/IGeoManagementProvider.cs b/Svrnty.GeoManagement.Abstractions/Abstractions/IGeoManagementProvider.cs new file mode 100644 index 0000000..735b231 --- /dev/null +++ b/Svrnty.GeoManagement.Abstractions/Abstractions/IGeoManagementProvider.cs @@ -0,0 +1,19 @@ +using Svrnty.GeoManagement.Abstractions.Models; + +namespace Svrnty.GeoManagement.Abstractions.Abstractions; + +public interface IGeoManagementProvider +{ + Task GetGeoPointAsync( + Address address, + CancellationToken cancellationToken = default); + + Task ReverseGeocodeAsync(GeoPoint geoPoint, + CancellationToken cancellationToken = default); + + Task NormalizeAddressAsync(Address address, + CancellationToken cancellationToken = default); + + Task NormalizeAddressAsync(string address, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Svrnty.GeoManagement.Abstractions/Models/Address.cs b/Svrnty.GeoManagement.Abstractions/Models/Address.cs new file mode 100644 index 0000000..6bd642e --- /dev/null +++ b/Svrnty.GeoManagement.Abstractions/Models/Address.cs @@ -0,0 +1,61 @@ +using Svrnty.GeoManagement.Abstractions.Abstractions; + +namespace Svrnty.GeoManagement.Abstractions.Models; + +public record GeoPoint(double Latitude, double Longitude); + +public record Address( + string Line1, + string? Line2, + string City, + string Subdivision, + string PostalCode, + string Country, + GeoPoint? Location, + string? Note, + bool IsNormalized = false +) : IAddress +{ + public string GetFormattedAddress( + FormattedAddressType formatType = FormattedAddressType.Full + ) + { + return formatType switch + { + FormattedAddressType.Full => FormatFullOneLine(), + FormattedAddressType.Compact => FormatCompactOneLine(), + FormattedAddressType.MultiLine => FormatMultiLine(), + _ => FormatFullOneLine() + }; + } + + private string FormatFullOneLine() + { + if (string.IsNullOrWhiteSpace(Line2)) + { + return $"{Line1}, {City}, {Subdivision} {PostalCode}, {Country}"; + } + + return $"{Line2}, {Line1}, {City}, {Subdivision} {PostalCode}, {Country}"; + } + + private string FormatCompactOneLine() + { + if (string.IsNullOrWhiteSpace(Line2)) + { + return $"{Line1}, {City}, {Subdivision}"; + } + + return $"{Line2}, {Line1}, {City}, {Subdivision}"; + } + + private string FormatMultiLine() + { + if (string.IsNullOrWhiteSpace(Line2)) + { + return $"{Line1}\n{City}, {Subdivision} {PostalCode}\n{Country}"; + } + + return $"{Line2}\n{Line1}\n{City}, {Subdivision} {PostalCode}\n{Country}"; + } +} \ No newline at end of file diff --git a/Svrnty.GeoManagement.Abstractions/Models/FormattedAddressType.cs b/Svrnty.GeoManagement.Abstractions/Models/FormattedAddressType.cs new file mode 100644 index 0000000..e35df75 --- /dev/null +++ b/Svrnty.GeoManagement.Abstractions/Models/FormattedAddressType.cs @@ -0,0 +1,8 @@ +namespace Svrnty.GeoManagement.Abstractions.Models; + +public enum FormattedAddressType +{ + Full, + Compact, + MultiLine +} \ No newline at end of file diff --git a/Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj b/Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/Svrnty.GeoManagement.Tests/Svrnty.GeoManagement.Tests.csproj b/Svrnty.GeoManagement.Tests/Svrnty.GeoManagement.Tests.csproj new file mode 100644 index 0000000..61c2910 --- /dev/null +++ b/Svrnty.GeoManagement.Tests/Svrnty.GeoManagement.Tests.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/Svrnty.GeoManagement.sln b/Svrnty.GeoManagement.sln new file mode 100644 index 0000000..8c1f166 --- /dev/null +++ b/Svrnty.GeoManagement.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svrnty.GeoManagement.Tests", "Svrnty.GeoManagement.Tests\Svrnty.GeoManagement.Tests.csproj", "{C2DE2776-E457-49B0-A3E2-E31AFE8C922C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svrnty.GeoManagement.Abstractions", "Svrnty.GeoManagement.Abstractions\Svrnty.GeoManagement.Abstractions.csproj", "{FB10FC74-7B99-4B3C-A671-6DF451157C98}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C2DE2776-E457-49B0-A3E2-E31AFE8C922C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2DE2776-E457-49B0-A3E2-E31AFE8C922C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2DE2776-E457-49B0-A3E2-E31AFE8C922C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2DE2776-E457-49B0-A3E2-E31AFE8C922C}.Release|Any CPU.Build.0 = Release|Any CPU + {FB10FC74-7B99-4B3C-A671-6DF451157C98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB10FC74-7B99-4B3C-A671-6DF451157C98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB10FC74-7B99-4B3C-A671-6DF451157C98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB10FC74-7B99-4B3C-A671-6DF451157C98}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal