Forge
csharpc88715b7
1using System.Text.Json.Nodes;
2using Tomlyn;
3using Tomlyn.Model;
4
5namespace AIGuiders.DotnetTools.TomlCheck;
6
7internal sealed class TomlCheckEngine(string? schemaOverride)
8{
9 public int CheckPaths(IEnumerable<string> paths)
10 {
11 var files = ExpandPaths(paths).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(p => p).ToList();
12 if (files.Count == 0)
13 {
14 Console.Error.WriteLine("No TOML files matched.");
15 return 1;
16 }
17
18 var failed = 0;
19 foreach (var file in files)
20 {
21 if (!CheckFile(file))
22 failed++;
23 }
24
25 if (failed > 0)
26 {
27 Console.Error.WriteLine();
28 Console.Error.WriteLine($"{failed} file(s) failed validation.");
29 return 1;
30 }
31
32 Console.WriteLine($"OK: {files.Count} file(s).");
33 return 0;
34 }
35
36 private bool CheckFile(string tomlPath)
37 {
38 var schemaPath = ResolveSchemaPath(tomlPath);
39 if (schemaPath is null)
40 {
41 Console.Error.WriteLine($"{tomlPath}: no #:schema directive (pass --schema <file.json>).");
42 return false;
43 }
44
45 if (!File.Exists(schemaPath))
46 {
47 Console.Error.WriteLine($"{tomlPath}: schema not found: {schemaPath}");
48 return false;
49 }
50
51 string text;
52 try
53 {
54 text = File.ReadAllText(tomlPath);
55 }
56 catch (Exception ex)
57 {
58 Console.Error.WriteLine($"{tomlPath}: read failed — {ex.Message}");
59 return false;
60 }
61
62 TomlTable table;
63 try
64 {
65 table = TomlSerializer.Deserialize<TomlTable>(text)
66 ?? throw new InvalidOperationException("Empty TOML document.");
67 }
68 catch (Exception ex)
69 {
70 Console.Error.WriteLine($"{tomlPath}: TOML parse error — {ex.Message}");
71 return false;
72 }
73
74 JsonNode document;
75 try
76 {
77 document = TomlToJsonConverter.ToJsonNode(table);
78 }
79 catch (Exception ex)
80 {
81 Console.Error.WriteLine($"{tomlPath}: TOML → JSON conversion failed — {ex.Message}");
82 return false;
83 }
84
85 JsonSchemaValidator validator;
86 try
87 {
88 validator = new JsonSchemaValidator(schemaPath);
89 }
90 catch (Exception ex)
91 {
92 Console.Error.WriteLine($"{tomlPath}: schema load failed — {ex.Message}");
93 return false;
94 }
95
96 if (validator.TryValidate(document, out var errors))
97 {
98 Console.WriteLine($"OK {tomlPath}");
99 return true;
100 }
101
102 Console.Error.WriteLine($"FAIL {tomlPath} (schema: {schemaPath})");
103 foreach (var error in errors)
104 Console.Error.WriteLine($" {error}");
105 return false;
106 }
107
108 private string? ResolveSchemaPath(string tomlPath)
109 {
110 if (!string.IsNullOrWhiteSpace(schemaOverride))
111 return Path.GetFullPath(schemaOverride!);
112
113 var directive = SchemaDirective.TryReadSchemaPath(tomlPath);
114 return directive is null
115 ? null
116 : SchemaDirective.ResolveSchemaPath(tomlPath, directive);
117 }
118
119 private static IEnumerable<string> ExpandPaths(IEnumerable<string> paths)
120 {
121 foreach (var raw in paths)
122 {
123 var path = Path.GetFullPath(raw);
124 if (File.Exists(path))
125 {
126 if (path.EndsWith(".toml", StringComparison.OrdinalIgnoreCase))
127 yield return path;
128 continue;
129 }
130
131 if (!Directory.Exists(path))
132 {
133 Console.Error.WriteLine($"Path not found: {path}");
134 continue;
135 }
136
137 foreach (var file in Directory.EnumerateFiles(path, "*.toml", SearchOption.AllDirectories))
138 yield return file;
139 }
140 }
141}
142
View only · write via MCP/CIDE