1
+ using McMaster . Extensions . CommandLineUtils ;
2
+ using Microsoft . CodeAnalysis ;
3
+ using Microsoft . CodeAnalysis . CSharp ;
4
+ using Microsoft . CodeAnalysis . Emit ;
5
+ using Microsoft . Extensions . DependencyInjection ;
6
+ using System . ComponentModel . DataAnnotations ;
7
+ using System . Reflection ;
8
+ using System . Text ;
9
+
10
+ namespace Resend . DocsCheck ;
11
+
12
+ /// <summary />
13
+ public class Program
14
+ {
15
+ /// <summary />
16
+ public static int Main ( string [ ] args )
17
+ {
18
+ /*
19
+ *
20
+ */
21
+ var app = new CommandLineApplication < Program > ( ) ;
22
+
23
+ var svc = new ServiceCollection ( ) ;
24
+
25
+ var sp = svc . BuildServiceProvider ( ) ;
26
+
27
+
28
+ /*
29
+ *
30
+ */
31
+ try
32
+ {
33
+ app . Conventions
34
+ . UseDefaultConventions ( )
35
+ . UseConstructorInjection ( sp ) ;
36
+ }
37
+ catch ( Exception ex )
38
+ {
39
+ Console . WriteLine ( "ftl: unhandled exception during setup" ) ;
40
+ Console . WriteLine ( ex . ToString ( ) ) ;
41
+
42
+ return 2 ;
43
+ }
44
+
45
+
46
+ /*
47
+ *
48
+ */
49
+ try
50
+ {
51
+ return app . Execute ( args ) ;
52
+ }
53
+ catch ( UnrecognizedCommandParsingException ex )
54
+ {
55
+ Console . WriteLine ( "err: " + ex . Message ) ;
56
+
57
+ return 2 ;
58
+ }
59
+ catch ( Exception ex )
60
+ {
61
+ Console . WriteLine ( "ftl: unhandled exception during execution" ) ;
62
+ Console . WriteLine ( ex . ToString ( ) ) ;
63
+
64
+ return 2 ;
65
+ }
66
+ }
67
+
68
+
69
+ /// <summary />
70
+ [ Option ( "-r|--root" , Description = "" ) ]
71
+ [ DirectoryExists ]
72
+ [ Required ]
73
+ public string ? RootFolder { get ; set ; }
74
+
75
+
76
+ /// <summary />
77
+ public async Task < int > OnExecute ( )
78
+ {
79
+ var dir = new DirectoryInfo ( this . RootFolder ! ) ;
80
+
81
+ foreach ( var f in dir . GetFiles ( "*.mdx" , SearchOption . AllDirectories ) )
82
+ {
83
+ await FileCheck ( dir , f ) ;
84
+ }
85
+
86
+ return 0 ;
87
+ }
88
+
89
+
90
+ /// <summary />
91
+ private async Task FileCheck ( DirectoryInfo root , FileInfo f )
92
+ {
93
+ var rel = GetRelativePath ( root , f ) ;
94
+ var mdx = await File . ReadAllTextAsync ( f . FullName ) ;
95
+
96
+
97
+ /*
98
+ *
99
+ */
100
+ var startIx = mdx . IndexOf ( "```csharp" ) ;
101
+
102
+ if ( startIx < 0 )
103
+ return ;
104
+
105
+ var endIx = mdx . IndexOf ( "```" , startIx + 4 ) ;
106
+
107
+
108
+ var fragment = mdx . Substring ( startIx , endIx - startIx + 3 ) ;
109
+
110
+
111
+ /*
112
+ *
113
+ */
114
+ var lines = fragment . Split ( "\n " ) ;
115
+
116
+ var sb = new StringBuilder ( ) ;
117
+ sb . AppendLine ( "using System;" ) ;
118
+ sb . AppendLine ( "using System.Threading.Tasks;" ) ;
119
+
120
+ foreach ( var l in lines )
121
+ if ( l . StartsWith ( "using " ) == true )
122
+ sb . AppendLine ( l ) ;
123
+
124
+ sb . AppendLine ( ) ;
125
+ sb . AppendLine ( "public class Program {" ) ;
126
+ sb . AppendLine ( "public static async Task<int> Main( string[] args ) {" ) ;
127
+
128
+ foreach ( var l in lines . Skip ( 1 ) . Take ( lines . Count ( ) - 2 ) )
129
+ {
130
+ if ( l . StartsWith ( "using " ) == true )
131
+ continue ;
132
+
133
+ sb . AppendLine ( l ) ;
134
+ }
135
+
136
+ sb . AppendLine ( "return 0; } }" ) ;
137
+
138
+ var csharp = sb . ToString ( ) ;
139
+
140
+
141
+ /*
142
+ *
143
+ */
144
+ SyntaxTree syntaxTree = CSharpSyntaxTree . ParseText ( csharp ) ;
145
+
146
+ var assemblyName = Path . GetRandomFileName ( ) ;
147
+ var references = new MetadataReference [ ]
148
+ {
149
+ MetadataReference . CreateFromFile ( typeof ( Object ) . Assembly . Location ) ,
150
+ MetadataReference . CreateFromFile ( Assembly . Load ( "System.Console" ) . Location ) ,
151
+ MetadataReference . CreateFromFile ( Assembly . Load ( "System.Runtime" ) . Location ) ,
152
+ MetadataReference . CreateFromFile ( Assembly . Load ( "System.Collections" ) . Location ) ,
153
+ MetadataReference . CreateFromFile ( typeof ( List < > ) . Assembly . Location ) ,
154
+ MetadataReference . CreateFromFile ( typeof ( IResend ) . Assembly . Location ) ,
155
+ } ;
156
+
157
+ CSharpCompilation compilation = CSharpCompilation . Create (
158
+ assemblyName ,
159
+ syntaxTrees : new [ ] { syntaxTree } ,
160
+ references : references ,
161
+ options : new CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary ) ) ;
162
+
163
+ using ( var ms = new MemoryStream ( ) )
164
+ {
165
+ EmitResult result = compilation . Emit ( ms ) ;
166
+
167
+ if ( result . Success == false )
168
+ {
169
+ // handle exceptions
170
+ IEnumerable < Diagnostic > failures = result . Diagnostics . Where ( diagnostic =>
171
+ diagnostic . IsWarningAsError ||
172
+ diagnostic . Severity == DiagnosticSeverity . Error ) ;
173
+
174
+ var fg = Console . ForegroundColor ;
175
+ Console . ForegroundColor = ConsoleColor . Red ;
176
+ Console . Write ( "err" ) ;
177
+ Console . ForegroundColor = fg ;
178
+
179
+ Console . Write ( " " ) ;
180
+ Console . WriteLine ( rel ) ;
181
+
182
+ foreach ( Diagnostic diagnostic in failures )
183
+ {
184
+ Console . Error . WriteLine ( "{0}: {1}" , diagnostic . Id , diagnostic . GetMessage ( ) ) ;
185
+ }
186
+ }
187
+ else
188
+ {
189
+ var fg = Console . ForegroundColor ;
190
+ Console . ForegroundColor = ConsoleColor . Green ;
191
+ Console . Write ( "aok" ) ;
192
+ Console . ForegroundColor = fg ;
193
+
194
+ Console . Write ( " " ) ;
195
+ Console . WriteLine ( rel ) ;
196
+ }
197
+ }
198
+ }
199
+
200
+
201
+ /// <summary />
202
+ private static string GetRelativePath ( DirectoryInfo directoryInfo , FileInfo fileInfo )
203
+ {
204
+ Uri directoryUri = new Uri ( directoryInfo . FullName + Path . DirectorySeparatorChar ) ;
205
+ Uri fileUri = new Uri ( fileInfo . FullName ) ;
206
+ Uri relativeUri = directoryUri . MakeRelativeUri ( fileUri ) ;
207
+ string relativePath = Uri . UnescapeDataString ( relativeUri . ToString ( ) ) ;
208
+
209
+ return relativePath . Replace ( '/' , Path . DirectorySeparatorChar ) ;
210
+ }
211
+ }
0 commit comments