feat(porttracker): add BatchFilters shared infrastructure
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
35
reports/report_3f6c5f2.md
Normal file
35
reports/report_3f6c5f2.md
Normal file
@@ -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%)**
|
||||
191
tools/NatsNet.PortTracker/Commands/BatchFilters.cs
Normal file
191
tools/NatsNet.PortTracker/Commands/BatchFilters.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using System.CommandLine;
|
||||
using NatsNet.PortTracker.Data;
|
||||
|
||||
namespace NatsNet.PortTracker.Commands;
|
||||
|
||||
public static class BatchFilters
|
||||
{
|
||||
public static Option<string?> IdsOption() => new("--ids")
|
||||
{
|
||||
Description = "ID range: 100-200, 1,5,10, or mixed 1-5,10,20-25"
|
||||
};
|
||||
|
||||
public static Option<int?> ModuleOption() => new("--module")
|
||||
{
|
||||
Description = "Filter by module ID"
|
||||
};
|
||||
|
||||
public static Option<string?> StatusOption() => new("--status")
|
||||
{
|
||||
Description = "Filter by current status"
|
||||
};
|
||||
|
||||
public static Option<bool> 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<int> ParseIds(string? idsSpec)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(idsSpec)) return [];
|
||||
|
||||
var ids = new List<int>();
|
||||
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<string>();
|
||||
var parameters = new List<(string name, object? value)>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(idsSpec))
|
||||
{
|
||||
var ids = ParseIds(idsSpec);
|
||||
if (ids.Count > 0)
|
||||
{
|
||||
var placeholders = new List<string>();
|
||||
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<long>(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<Dictionary<string, object?>> 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)] + "..";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user