- Created 52 Gitea issues across milestones 1-7, one per completion criterion - Each criterion now links to its corresponding issue ([#N](url) format) - Milestone Tracking sections updated with Issue Updates subsection: when/how to comment, close issues via CLI or web UI - Phases 4-7 criteria converted from plain bullets to checkbox format
14 KiB
Phase 3: Library Mapping
Objective
Map every external Go dependency detected by the analyzer to its .NET equivalent.
This includes Go standard library packages, well-known third-party libraries, and
NATS ecosystem packages. When Phase 3 is complete, the library suggest command
returns an empty list and every import has a documented .NET migration path.
Prerequisites
- Phases 1 and 2 are complete (database is populated and verified).
- Familiarity with both the Go standard library and .NET BCL / NuGet ecosystem.
- A working
dotnetCLI for running PortTracker commands.
Source and Target Locations
| Component | Path |
|---|---|
| Go source code | golang/ (specifically golang/nats-server/) |
| .NET ported version | dotnet/ |
Milestone Tracking
This phase corresponds to Milestone 3 in Gitea. When starting this phase, verify the milestone is open. Assign relevant issues to this milestone as work progresses.
Issue Updates
Each completion criterion has a corresponding Gitea issue. Update issues as you work:
- Starting a criterion: Add a comment noting work has begun
- Blocked: Add a comment describing the blocker
- Complete: Close the issue with a comment summarizing the result (e.g., counts, verification output)
Close issues via CLI:
curl -s -X PATCH "https://gitea.dohertylan.com/api/v1/repos/dohertj2/natsnet/issues/{N}" \
-H "Content-Type: application/json" \
-H "Authorization: token $GITEA_TOKEN" \
-d '{"state":"closed"}'
Or close via the Gitea web UI.
Steps
Step 1: List all unmapped libraries
Start by seeing everything that needs attention:
dotnet run --project tools/NatsNet.PortTracker -- library suggest --db porting.db
This shows all library_mappings entries with status not_mapped, sorted by import
path. The output includes the Go import path, the library name, and a usage
description.
If the list is empty, all libraries are already mapped and Phase 3 is complete.
Step 2: Review the full library list
To see both mapped and unmapped libraries in one view:
dotnet run --project tools/NatsNet.PortTracker -- library list --db porting.db
You can also filter by status:
dotnet run --project tools/NatsNet.PortTracker -- library list --status not_mapped --db porting.db
dotnet run --project tools/NatsNet.PortTracker -- library list --status mapped --db porting.db
Step 3: Map each library
For each unmapped library, determine the appropriate .NET equivalent using the reference table below, then record the mapping:
dotnet run --project tools/NatsNet.PortTracker -- library map <id> \
--package "<NuGet package or BCL>" \
--namespace "<.NET namespace>" \
--notes "<migration notes>" \
--db porting.db
Example -- mapping encoding/json:
dotnet run --project tools/NatsNet.PortTracker -- library map 1 \
--package "System.Text.Json" \
--namespace "System.Text.Json" \
--notes "Use JsonSerializer. Consider source generators for AOT." \
--db porting.db
Example -- mapping github.com/klauspost/compress:
dotnet run --project tools/NatsNet.PortTracker -- library map 12 \
--package "System.IO.Compression" \
--namespace "System.IO.Compression" \
--notes "S2/Snappy codec needs evaluation; may need custom impl or IronSnappy NuGet." \
--db porting.db
Repeat for every entry in the library suggest output.
Step 4: Handle libraries that need custom implementations
Some Go libraries have no direct .NET equivalent and will require custom code. For these, record the mapping with a descriptive note:
dotnet run --project tools/NatsNet.PortTracker -- library map <id> \
--package "Custom" \
--namespace "NatsNet.Internal" \
--notes "No direct equivalent; requires custom implementation. See <details>." \
--db porting.db
Step 5: Verify all libraries are mapped
Run the suggest command again -- it should return an empty list:
dotnet run --project tools/NatsNet.PortTracker -- library suggest --db porting.db
Expected output:
All libraries have been mapped!
Also verify via the full list:
dotnet run --project tools/NatsNet.PortTracker -- library list --db porting.db
Every entry should show status mapped (or verified if you have already
validated the mapping in code).
Step 6: Run the phase check
Confirm Phase 1 now shows full completion including library mappings:
dotnet run --project tools/NatsNet.PortTracker -- phase check 1 --db porting.db
Expected:
Phase 1 Checklist:
[x] Modules populated: <N>
[x] Features populated: <N>
[x] Unit tests populated: <N>
[x] Dependencies mapped: <N>
[x] Libraries identified: <N>
[x] All libraries mapped: <N>/<N>
Step 7: Export the mapping report
Save the complete library mapping state for reference during porting:
dotnet run --project tools/NatsNet.PortTracker -- report export \
--format md \
--output docs/reports/phase-3-library-mapping.md \
--db porting.db
Common Go to .NET Mappings Reference
Use this table as a starting point when determining .NET equivalents. Adapt based on the specific usage patterns found in the NATS server source.
Go Standard Library
| Go Package | .NET Equivalent | Notes |
|---|---|---|
encoding/json |
System.Text.Json |
Use JsonSerializer; consider source generators for performance |
encoding/binary |
System.Buffers.Binary.BinaryPrimitives |
For endian-aware reads/writes |
encoding/base64 |
System.Convert |
Convert.ToBase64String / Convert.FromBase64String |
encoding/hex |
System.Convert |
Convert.ToHexString (.NET 5+) |
encoding/pem |
System.Security.Cryptography.PemEncoding |
.NET 5+ |
sync |
System.Threading |
Mutex -> lock / Monitor; RWMutex -> ReaderWriterLockSlim; WaitGroup -> CountdownEvent; Once -> Lazy<T> |
sync/atomic |
System.Threading.Interlocked |
Interlocked.Increment, CompareExchange, etc. |
net |
System.Net.Sockets |
TcpListener, TcpClient, Socket |
net/http |
System.Net.Http / Microsoft.AspNetCore |
Client: HttpClient; Server: Kestrel / minimal APIs |
net/url |
System.Uri |
Uri, UriBuilder |
crypto/tls |
System.Net.Security.SslStream |
Wrap NetworkStream with SslStream |
crypto/x509 |
System.Security.Cryptography.X509Certificates |
X509Certificate2 |
crypto/sha256 |
System.Security.Cryptography.SHA256 |
SHA256.HashData() (.NET 8+) |
crypto/ed25519 |
System.Security.Cryptography |
Ed25519 support in .NET 9+ |
crypto/rand |
System.Security.Cryptography.RandomNumberGenerator |
RandomNumberGenerator.Fill() |
time |
System.TimeSpan / System.Threading.Timer |
time.Duration -> TimeSpan; time.Ticker -> PeriodicTimer; time.After -> Task.Delay |
time (parsing) |
System.DateTime / System.DateTimeOffset |
DateTime.Parse, custom formats |
fmt |
String interpolation / String.Format |
$"..." for most cases; String.Format for dynamic |
io |
System.IO |
Reader -> Stream; Writer -> Stream; io.Copy -> Stream.CopyTo |
io/fs |
System.IO |
Directory, File, FileInfo |
bufio |
System.IO.BufferedStream |
Or StreamReader / StreamWriter |
bytes |
System.Buffers / MemoryStream |
bytes.Buffer -> MemoryStream or ArrayBufferWriter<byte> |
strings |
System.String / System.Text.StringBuilder |
Most methods have direct equivalents |
strconv |
int.Parse, double.Parse, etc. |
Or Convert class |
context |
CancellationToken |
context.Context -> CancellationToken; context.WithCancel -> CancellationTokenSource |
os |
System.Environment / System.IO |
os.Exit -> Environment.Exit; file ops -> File class |
os/signal |
System.Runtime.Loader.AssemblyLoadContext |
UnloadingEvent or Console.CancelKeyPress |
path/filepath |
System.IO.Path |
Path.Combine, Path.GetDirectoryName, etc. |
sort |
System.Linq / Array.Sort |
LINQ .OrderBy() or in-place Array.Sort |
math |
System.Math |
Direct equivalent |
math/rand |
System.Random |
Random.Shared for thread-safe usage (.NET 6+) |
regexp |
System.Text.RegularExpressions.Regex |
Consider source generators for compiled patterns |
errors |
System.Exception |
Go errors -> .NET exceptions; errors.Is -> pattern matching |
log |
Serilog |
Project choice: Serilog via Microsoft.Extensions.Logging |
testing |
xUnit |
testing.T -> xUnit [Fact]/[Theory]; testing.B -> BenchmarkDotNet |
flag |
System.CommandLine |
Or Microsoft.Extensions.Configuration |
embed |
Embedded resources | .csproj <EmbeddedResource> items |
runtime |
System.Runtime / System.Environment |
runtime.GOOS -> RuntimeInformation.IsOSPlatform |
NATS Ecosystem Libraries
| Go Package | .NET Equivalent | Notes |
|---|---|---|
github.com/nats-io/jwt/v2 |
Custom / evaluate existing | JWT claims for NATS auth; may need custom implementation matching NATS JWT spec |
github.com/nats-io/nkeys |
Custom implementation | Ed25519 key pairs for NATS authentication; use System.Security.Cryptography Ed25519 |
github.com/nats-io/nuid |
Custom / System.Guid |
NATS unique IDs; simple custom implementation or adapt to Guid if format is flexible |
github.com/nats-io/nats.go |
NATS.Net (official) |
Only used in tests; the official .NET NATS client |
Third-Party Libraries
| Go Package | .NET Equivalent | Notes |
|---|---|---|
github.com/klauspost/compress |
System.IO.Compression |
General compression: GZipStream, DeflateStream. S2/Snappy: evaluate IronSnappy NuGet or custom port |
github.com/minio/highwayhash |
Custom / NuGet | HighwayHash implementation; search NuGet or port the algorithm |
golang.org/x/crypto |
System.Security.Cryptography |
bcrypt -> Rfc2898DeriveBytes or BCrypt.Net NuGet; argon2 -> Konscious.Security NuGet |
golang.org/x/sys |
System.Runtime.InteropServices |
Platform-specific syscalls -> P/Invoke or RuntimeInformation |
golang.org/x/time |
System.Threading.RateLimiting |
rate.Limiter -> RateLimiter (.NET 7+); TokenBucketRateLimiter or SlidingWindowRateLimiter |
golang.org/x/text |
System.Globalization |
Unicode normalization, encoding detection |
Mapping Decision Guidelines
When choosing a .NET equivalent, follow these priorities:
- BCL first: Prefer built-in .NET Base Class Library types over NuGet packages.
- Official packages second: If BCL does not cover it, prefer
Microsoft.*orSystem.*NuGet packages. - Well-maintained NuGet third: Choose packages with active maintenance, high download counts, and compatible licenses.
- Custom implementation last: Only write custom code when no suitable package exists. Document the rationale in the mapping notes.
For each mapping, consider:
- API surface: Does the .NET equivalent cover all methods used in the Go code?
- Performance: Are there performance-critical paths that need benchmarking?
- Thread safety: Go's concurrency model differs from .NET. Note any synchronization concerns.
- Platform support: Does the .NET package work on all target platforms (Linux, macOS, Windows)?
Completion Criteria
Phase 3 is complete when ALL of the following are true:
- #30
library suggestreturns "All libraries have been mapped!" - #21 Every entry in
library listshows statusmappedorverified. - #22 Each mapping includes a
--package(the NuGet package or BCL assembly), a--namespace(the .NET namespace to use), and--notes(migration guidance). - #23
phase check 1shows all items checked including "All libraries mapped". - #24 A mapping report has been exported for reference.
- Close the Phase 3 milestone in Gitea:
Or close it via the Gitea web UI at https://gitea.dohertylan.com/dohertj2/natsnet/milestone/3
curl -s -X PATCH "https://gitea.dohertylan.com/api/v1/repos/dohertj2/natsnet/milestones/3" \ -H "Content-Type: application/json" \ -H "Authorization: token $GITEA_TOKEN" \ -d '{"state":"closed"}'
Troubleshooting
"Library not found"
The ID you passed to library map does not exist. Run library suggest to get
the current list of IDs and their import paths.
Unsure which .NET package to use
For unfamiliar Go packages:
- Check what the package does in the Go source (look at the import usage in the files listed by the analyzer).
- Search NuGet.org for equivalent functionality.
- Check if the Go package is a thin wrapper around a well-known algorithm that .NET implements natively.
- When in doubt, map it as "Custom" with detailed notes and revisit during the porting phase.
Multiple .NET options for one Go package
When there are several valid .NET equivalents (e.g., Newtonsoft.Json vs
System.Text.Json), prefer the one that:
- Is part of the BCL or a Microsoft package.
- Has better performance characteristics.
- Has source generator support for AOT compilation.
Record the alternatives in the --notes field so the decision can be revisited.
Stdlib packages showing as unmapped
The analyzer classifies imports as stdlib vs external based on whether the first
path component contains a dot. Standard library packages like encoding/json,
net/http, etc. should still be recorded in the library_mappings table so that
every import path has a documented .NET migration path. Map them using the
reference table above.