diff --git a/reports/current.md b/reports/current.md index d99bebb..7e510cb 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-27 09:38:59 UTC +Generated: 2026-02-27 09:40:28 UTC ## Modules (12 total) diff --git a/reports/report_3f6c5f2.md b/reports/report_3f6c5f2.md new file mode 100644 index 0000000..7e510cb --- /dev/null +++ b/reports/report_3f6c5f2.md @@ -0,0 +1,35 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-27 09:40:28 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| verified | 12 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| deferred | 3394 | +| verified | 279 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| deferred | 2680 | +| n_a | 187 | +| verified | 390 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**868/6942 items complete (12.5%)** diff --git a/tools/NatsNet.PortTracker/Commands/BatchFilters.cs b/tools/NatsNet.PortTracker/Commands/BatchFilters.cs new file mode 100644 index 0000000..68115d9 --- /dev/null +++ b/tools/NatsNet.PortTracker/Commands/BatchFilters.cs @@ -0,0 +1,191 @@ +using System.CommandLine; +using NatsNet.PortTracker.Data; + +namespace NatsNet.PortTracker.Commands; + +public static class BatchFilters +{ + public static Option IdsOption() => new("--ids") + { + Description = "ID range: 100-200, 1,5,10, or mixed 1-5,10,20-25" + }; + + public static Option ModuleOption() => new("--module") + { + Description = "Filter by module ID" + }; + + public static Option StatusOption() => new("--status") + { + Description = "Filter by current status" + }; + + public static Option ExecuteOption() => new("--execute") + { + Description = "Actually apply changes (default is dry-run preview)", + DefaultValueFactory = _ => false + }; + + public static void AddFilterOptions(Command cmd, bool includeModuleFilter) + { + cmd.Add(IdsOption()); + if (includeModuleFilter) + cmd.Add(ModuleOption()); + cmd.Add(StatusOption()); + cmd.Add(ExecuteOption()); + } + + public static List ParseIds(string? idsSpec) + { + if (string.IsNullOrWhiteSpace(idsSpec)) return []; + + var ids = new List(); + foreach (var part in idsSpec.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + { + if (part.Contains('-')) + { + var range = part.Split('-', 2); + if (int.TryParse(range[0], out var start) && int.TryParse(range[1], out var end)) + { + for (var i = start; i <= end; i++) + ids.Add(i); + } + else + { + Console.WriteLine($"Warning: invalid range '{part}', skipping."); + } + } + else if (int.TryParse(part, out var id)) + { + ids.Add(id); + } + else + { + Console.WriteLine($"Warning: invalid ID '{part}', skipping."); + } + } + return ids; + } + + public static (string whereClause, List<(string name, object? value)> parameters) BuildWhereClause( + string? idsSpec, int? moduleId, string? status, string idColumn = "id", string moduleColumn = "module_id") + { + var clauses = new List(); + var parameters = new List<(string name, object? value)>(); + + if (!string.IsNullOrWhiteSpace(idsSpec)) + { + var ids = ParseIds(idsSpec); + if (ids.Count > 0) + { + var placeholders = new List(); + for (var i = 0; i < ids.Count; i++) + { + placeholders.Add($"@id{i}"); + parameters.Add(($"@id{i}", ids[i])); + } + clauses.Add($"{idColumn} IN ({string.Join(", ", placeholders)})"); + } + } + + if (moduleId is not null) + { + clauses.Add($"{moduleColumn} = @moduleFilter"); + parameters.Add(("@moduleFilter", moduleId)); + } + + if (!string.IsNullOrWhiteSpace(status)) + { + clauses.Add("status = @statusFilter"); + parameters.Add(("@statusFilter", status)); + } + + if (clauses.Count == 0) + return ("", parameters); + + return (" WHERE " + string.Join(" AND ", clauses), parameters); + } + + public static void PreviewOrExecute( + Database db, + string table, + string displayColumns, + string updateSetClause, + List<(string name, object? value)> updateParams, + string whereClause, + List<(string name, object? value)> filterParams, + bool execute) + { + // Count matching rows + var countSql = $"SELECT COUNT(*) FROM {table}{whereClause}"; + var count = db.ExecuteScalar(countSql, filterParams.ToArray()); + + if (count == 0) + { + Console.WriteLine("No items match the specified filters."); + return; + } + + // Preview + var previewSql = $"SELECT {displayColumns} FROM {table}{whereClause} ORDER BY id"; + var rows = db.Query(previewSql, filterParams.ToArray()); + + if (!execute) + { + Console.WriteLine($"Would affect {count} items:"); + Console.WriteLine(); + PrintPreviewTable(rows); + Console.WriteLine(); + Console.WriteLine("Add --execute to apply these changes."); + return; + } + + // Execute + var allParams = new List<(string name, object? value)>(); + allParams.AddRange(updateParams); + allParams.AddRange(filterParams); + + var updateSql = $"UPDATE {table} SET {updateSetClause}{whereClause}"; + var affected = db.ExecuteInTransaction(updateSql, allParams.ToArray()); + Console.WriteLine($"Updated {affected} items."); + } + + private static void PrintPreviewTable(List> rows) + { + if (rows.Count == 0) return; + + var columns = rows[0].Keys.ToList(); + var widths = columns.Select(c => c.Length).ToList(); + + foreach (var row in rows) + { + for (var i = 0; i < columns.Count; i++) + { + var val = row[columns[i]]?.ToString() ?? ""; + if (val.Length > widths[i]) widths[i] = Math.Min(val.Length, 40); + } + } + + // Header + var header = string.Join(" ", columns.Select((c, i) => Truncate(c, widths[i]).PadRight(widths[i]))); + Console.WriteLine(header); + Console.WriteLine(new string('-', header.Length)); + + // Rows (cap at 50 for preview) + var displayRows = rows.Take(50).ToList(); + foreach (var row in displayRows) + { + var line = string.Join(" ", columns.Select((c, i) => + Truncate(row[c]?.ToString() ?? "", widths[i]).PadRight(widths[i]))); + Console.WriteLine(line); + } + + if (rows.Count > 50) + Console.WriteLine($" ... and {rows.Count - 50} more"); + } + + private static string Truncate(string s, int maxLen) + { + return s.Length <= maxLen ? s : s[..(maxLen - 2)] + ".."; + } +}