Skip to content

Commit

Permalink
Update generation and error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
pomianowski committed May 30, 2024
1 parent a9d188a commit f243add
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 1 deletion.
86 changes: 85 additions & 1 deletion src/OpenApi.Client.Cli/Commands/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// Copyright (C) Leszek Pomianowski and OpenAPI Client Contributors.
// All Rights Reserved.

using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using OpenApi.Client.Cli.Settings;
using OpenApi.Client.SourceGenerators.Contracts;
using OpenApi.Client.SourceGenerators.Genertion;
using OpenApi.Client.SourceGenerators.Schema;
using OpenApi.Client.SourceGenerators.Serialization;

Expand All @@ -22,17 +24,73 @@ public sealed class GenerateCommand : AsyncCommand<GenerateCommandSettings>
{
private static readonly Regex PathNormalizer = new(@"(\\\\|//)", RegexOptions.Compiled);

private static readonly Regex NamespaceValidator =
new(@"^[_a-zA-Z][_a-zA-Z0-9]*(\.[_a-zA-Z][_a-zA-Z0-9]*)*$", RegexOptions.Compiled);

private static readonly Regex ClassNameValidator =
new(@"^[_a-zA-Z][_a-zA-Z0-9]*$", RegexOptions.Compiled);

/// <inheritdoc />
public override async Task<int> ExecuteAsync(
CommandContext context,
GenerateCommandSettings settings
)
{
using CancellationTokenSource cancellationTokenSource = new();
string contents = await File.ReadAllTextAsync(settings.File);

SerializationResult<IApiDocument>? serializationResult =
new OpenApiSerializer().Deserialize(settings.File, contents);

if (serializationResult.HasErrors)
{
foreach (
SerializationResultError serializationResultError in serializationResult.Errors
)
{
AnsiConsole.MarkupLine($"[red]Error: {serializationResultError.Message}[/]");
}

return -1;
}

if (serializationResult.Result is null)
{
AnsiConsole.MarkupLine($"[red]Error: Serialized JSON returned empty API.[/]");

return -2;
}

string? generatedSource = null;

OpenApiContract contract = OpenApiContractParser.Parse(
settings.Namespace,
settings.ClassName,
Accessibility.Public,
serializationResult.Result
);

ClientGenerator generator = new(contract);
GenerationResult<string> generatorResult = generator.Generate();

if (generatorResult.HasErrors)
{
foreach (GenerationResultError generatorResultError in generatorResult.Errors)
{
AnsiConsole.MarkupLine($"[red]Error: {generatorResultError.Message}[/]");
}

return -1;
}

generatedSource = generatorResult.Result;

await File.WriteAllTextAsync(
settings.Output,
generatedSource,
cancellationTokenSource.Token
);

return 0;
}

Expand All @@ -42,6 +100,29 @@ public override ValidationResult Validate(
GenerateCommandSettings settings
)
{
if (string.IsNullOrEmpty(settings.ClassName))
{
settings.ClassName = Path.GetFileNameWithoutExtension(settings.Output);
}

settings.ClassName = settings.ClassName.Trim();

if (!ClassNameValidator.IsMatch(settings.ClassName))
{
return ValidationResult.Error(
$"The name '{settings.ClassName}' is not valid C# type name."
);
}

settings.Namespace = settings.Namespace.Trim();

if (!NamespaceValidator.IsMatch(settings.Namespace))
{
return ValidationResult.Error(
$"The namespace '{settings.Namespace}' is not valid C# namespace."
);
}

settings.File = PathNormalizer.Replace(settings.File, "/");
settings.File = settings.File.Replace("\\", "/");

Expand All @@ -50,6 +131,9 @@ GenerateCommandSettings settings
return ValidationResult.Error($"The file '{settings.File}' does not exist.");
}

settings.Output = PathNormalizer.Replace(settings.Output, "/");
settings.Output = settings.Output.Replace("\\", "/");

return ValidationResult.Success();
}
}
14 changes: 14 additions & 0 deletions src/OpenApi.Client.Cli/Settings/GenerateCommandSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ public sealed class GenerateCommandSettings : CommandSettings
[Description("The output directory.")]
public string Output { get; set; } = "./";

/// <summary>
/// Gets or sets the name of the generated class.
/// </summary>
[CommandOption("-c|--classname <NAMESPACE>")]
[Description("The name of the generated class..")]
public string ClassName { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the namespace for the generated class.
/// </summary>
[CommandOption("-n|--namespace <NAMESPACE>")]
[Description("The namespace in which the class is to be placed.")]
public string Namespace { get; set; } = "OpenApi";

/// <summary>
/// Gets or sets the JSON serializer to use. If not provided, System.Text.Json is used.
/// </summary>
Expand Down

0 comments on commit f243add

Please sign in to comment.