diff --git a/src/ApiGenerator/Generator/ApiGenerator.cs b/src/ApiGenerator/Generator/ApiGenerator.cs index dfa87f38ed..d1ee1eb6fa 100644 --- a/src/ApiGenerator/Generator/ApiGenerator.cs +++ b/src/ApiGenerator/Generator/ApiGenerator.cs @@ -29,7 +29,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Dynamic; using System.IO; using System.Linq; using System.Threading; @@ -37,7 +36,6 @@ using ApiGenerator.Configuration; using ApiGenerator.Domain; using ApiGenerator.Domain.Code; -using ApiGenerator.Domain.Specification; using ApiGenerator.Generator.Razor; using NJsonSchema; using NSwag; @@ -49,6 +47,7 @@ namespace ApiGenerator.Generator public class ApiGenerator { public static List Warnings { get; private set; } = new(); + public static HashSet GeneratedFilePaths = []; public static async Task Generate(bool lowLevelOnly, RestApiSpec spec, CancellationToken token) { diff --git a/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs b/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs index 4d40ca84e4..b242522af5 100644 --- a/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs +++ b/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs @@ -32,7 +32,6 @@ using System.Threading; using System.Threading.Tasks; using ApiGenerator.Domain; -using CSharpier; using RazorLight; using RazorLight.Generation; using RazorLight.Razor; @@ -44,9 +43,9 @@ public abstract class RazorGeneratorBase { private static readonly RazorLightEngine Engine = new RazorLightEngineBuilder() .UseProject(new EmbeddedRazorProject(typeof(CodeTemplatePage<>).Assembly, "ApiGenerator.Views")) - .SetOperatingAssembly(typeof(CodeTemplatePage<>).Assembly) - .UseMemoryCachingProvider() - .EnableDebugMode() + .SetOperatingAssembly(typeof(CodeTemplatePage<>).Assembly) + .UseMemoryCachingProvider() + .EnableDebugMode() .Build(); protected static async Task DoRazor(TModel model, string viewLocation, string targetLocation, CancellationToken token) @@ -54,12 +53,13 @@ protected static async Task DoRazor(TModel model, string viewLocation, s try { token.ThrowIfCancellationRequested(); - var generated = await Engine.CompileRenderAsync(viewLocation, model); + var generated = await Engine.CompileRenderAsync(viewLocation, model); await WriteFormattedCsharpFile(targetLocation, generated); } catch (TemplateGenerationException e) { - foreach (var d in e.Diagnostics) Console.WriteLine(d.GetMessage()); + foreach (var d in e.Diagnostics) + Console.WriteLine(d.GetMessage()); throw; } } @@ -69,31 +69,37 @@ protected async Task DoRazorDependantFiles( Func identifier, Func target, CancellationToken token ) - { - using var c = pbar.Spawn(items.Count, "Generating namespaces", new ProgressBarOptions - { - ProgressCharacter = '─', - ForegroundColor = ConsoleColor.Yellow - }); - foreach (var item in items) - { - var id = identifier(item); - var targetLocation = target(id); - await DoRazor(item, viewLocation, targetLocation, token); - c.Tick($"{Title}: {id}"); - } - } + { + using var c = pbar.Spawn(items.Count, "Generating namespaces", new ProgressBarOptions + { + ProgressCharacter = '─', + ForegroundColor = ConsoleColor.Yellow + }); + foreach (var item in items) + { + var id = identifier(item); + var targetLocation = target(id); + await DoRazor(item, viewLocation, targetLocation, token); + c.Tick($"{Title}: {id}"); + } + } private static async Task WriteFormattedCsharpFile(string path, string contents) { - contents = (await CodeFormatter.FormatAsync(contents)).Code; + if (Directory.GetParent(path) is { Exists: false } dir) + dir.Create(); + + var directory = Directory.GetParent(path); + ApiGenerator.GeneratedFilePaths.Add($"{directory.FullName}\\"); //we must have a trailing slash - if (Directory.GetParent(path) is { Exists: false } dir) dir.Create(); + await File.WriteAllTextAsync(path, contents); + } - await File.WriteAllTextAsync(path, contents); + public abstract string Title + { + get; } - public abstract string Title { get; } public abstract Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token); } } diff --git a/src/ApiGenerator/Program.cs b/src/ApiGenerator/Program.cs index 4a219ddf11..e310025447 100644 --- a/src/ApiGenerator/Program.cs +++ b/src/ApiGenerator/Program.cs @@ -27,6 +27,8 @@ */ using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -126,7 +128,8 @@ private static async Task Generate(bool download, string branch, bool inclu await RestSpecDownloader.DownloadAsync(downloadBranch, token); } - if (!generateCode) return 0; + if (!generateCode) + return 0; Console.WriteLine(); AnsiConsole.Write(new Rule("[b white on chartreuse4] Loading specification [/]").LeftJustified()); @@ -150,6 +153,8 @@ private static async Task Generate(bool download, string branch, bool inclu await Generator.ApiGenerator.Generate(lowLevelOnly, spec, token); + RunDotNetFormat(); + var warnings = Generator.ApiGenerator.Warnings; if (warnings.Count > 0) { @@ -164,9 +169,63 @@ private static async Task Generate(bool download, string branch, bool inclu return 0; } + private static void RunDotNetFormat() + { + var enviromentPath = System.Environment.GetEnvironmentVariable("PATH"); + var paths = enviromentPath.Split(';'); + var exePath = paths.Select(x => Path.Combine(x, "dotnet.exe")) + .Where(x => File.Exists(x)) + .FirstOrDefault(); + + Console.WriteLine(); + AnsiConsole.Write(new Rule("[b white on chartreuse4] Formatting Code using dotnet format [/]").LeftJustified()); + Console.WriteLine(); + + var rootPath = GetRootPath(typeof(Program).Assembly.Location); + + var relativeFolderList = new List(); + + foreach (var item in Generator.ApiGenerator.GeneratedFilePaths) + { + var relativePath = item.Replace(rootPath.FullName, string.Empty); + relativeFolderList.Add($".{relativePath.Replace("\\", "/")}"); + } + + var si = new System.Diagnostics.ProcessStartInfo + { + WorkingDirectory = rootPath.FullName, + FileName = "dotnet", + Arguments = "format -v diag --include " + string.Join(' ', relativeFolderList.Select(item => $"{item}")), + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + var process = System.Diagnostics.Process.Start(si); + + Console.WriteLine(); + Console.WriteLine($"Running dotnet format --include {string.Join(' ', relativeFolderList.Select(item => $"{item}"))} -v diag"); + + // hookup the eventhandlers to capture the data that is received + process.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data); + process.ErrorDataReceived += (sender, args) => Console.WriteLine(args.Data); + + process.Start(); + + // start our event pumps + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + process.WaitForExit(); + Console.WriteLine(); + } + private static bool Ask(string question, bool defaultAnswer = true) { - if (!Interactive) return defaultAnswer; + if (!Interactive) + return defaultAnswer; var answer = "invalid"; var defaultResponse = defaultAnswer ? "y" : "n"; @@ -175,10 +234,35 @@ private static bool Ask(string question, bool defaultAnswer = true) { Console.Write($"{question}[y/N] (default {defaultResponse}): "); answer = Console.ReadLine()?.Trim().ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(answer)) answer = defaultResponse; + if (string.IsNullOrWhiteSpace(answer)) + answer = defaultResponse; defaultAnswer = answer == "y"; } return defaultAnswer; } + + /// + /// Since we are most likely not running in the root of the project, we need to find the root path that contains the sln + /// + /// + /// + private static DirectoryInfo GetRootPath(string basePath) => RecursiveFindRoot(new FileInfo(basePath).Directory); + + private static DirectoryInfo RecursiveFindRoot(DirectoryInfo directory) + { + if (directory is null) + { + return null; + } + + var file = directory.GetFiles("*.sln").FirstOrDefault(item => item.Name.Equals("OpenSearch.sln", StringComparison.OrdinalIgnoreCase)); + + if (file is not null) + { + return directory; + } + + return RecursiveFindRoot(directory.Parent); + } } }