diff --git a/src/SuperFluid.Tests/DemoApiDefinition.fluid.yml b/src/SuperFluid.Tests/DemoApiDefinition.fluid.yml index 597c1b8..9d47628 100644 --- a/src/SuperFluid.Tests/DemoApiDefinition.fluid.yml +++ b/src/SuperFluid.Tests/DemoApiDefinition.fluid.yml @@ -22,10 +22,16 @@ Methods: - "Enter" - Name: "Start" Arguments: - - Name: "speed" - Type: "int" + # These are deliberately out of order to test that the parser sticks the defaults to the end of the argument list - Name: "direction" Type: "string" + DefaultValue: "\"Forward\"" # Note that we need the quotes here + - Name: "speed" + Type: "int" + - Name: "hotwire" + Type: "bool" + DefaultValue: "false" + # These constraints are pointless but are here to test the parser GenericArguments: - Name: "T" diff --git a/src/SuperFluid.Tests/SourceGenerators/FluidGeneratorServiceTests.cs b/src/SuperFluid.Tests/SourceGenerators/FluidGeneratorServiceTests.cs index f327336..606e5e8 100644 --- a/src/SuperFluid.Tests/SourceGenerators/FluidGeneratorServiceTests.cs +++ b/src/SuperFluid.Tests/SourceGenerators/FluidGeneratorServiceTests.cs @@ -46,7 +46,7 @@ namespace SuperFluid.Tests.Cars; public interface ICanStartOrExit { - public ICanStopOrBuild Start(int speed, string direction) where T : class, INumber where X : notnull; + public ICanStopOrBuild Start(int speed, string direction = "Forward", bool hotwire = false) where T : class, INumber where X : notnull; public ICanLockOrEnter Exit(); } """; diff --git a/src/SuperFluid/Internal/Definitions/FluidApiArgumentDefinition.cs b/src/SuperFluid/Internal/Definitions/FluidApiArgumentDefinition.cs index ab8812e..e1062cd 100644 --- a/src/SuperFluid/Internal/Definitions/FluidApiArgumentDefinition.cs +++ b/src/SuperFluid/Internal/Definitions/FluidApiArgumentDefinition.cs @@ -7,4 +7,6 @@ internal record FluidApiArgumentDefinition { public required string Type { get; init; } public required string Name { get; init; } + + public string? DefaultValue { get; init; } } diff --git a/src/SuperFluid/Internal/Model/FluidApiArgument.cs b/src/SuperFluid/Internal/Model/FluidApiArgument.cs index 4c24fc4..00c76df 100644 --- a/src/SuperFluid/Internal/Model/FluidApiArgument.cs +++ b/src/SuperFluid/Internal/Model/FluidApiArgument.cs @@ -5,12 +5,15 @@ namespace SuperFluid.Internal.Model; [DebuggerDisplay("{Type} {Name}")] internal class FluidApiArgument { - public FluidApiArgument(string argName, string argType) + public FluidApiArgument(string argName, string argType, string? defaultValue) { Name = argName; Type = argType; + DefaultValue = defaultValue; } public string Type { get; init; } public string Name { get; init; } + + public string? DefaultValue { get; init; } } diff --git a/src/SuperFluid/Internal/Model/FluidApiMethod.cs b/src/SuperFluid/Internal/Model/FluidApiMethod.cs index 25c0bf4..4cf9a65 100644 --- a/src/SuperFluid/Internal/Model/FluidApiMethod.cs +++ b/src/SuperFluid/Internal/Model/FluidApiMethod.cs @@ -5,22 +5,26 @@ namespace SuperFluid.Internal.Model; [DebuggerDisplay("{Name}")] internal record FluidApiMethod { - public FluidApiMethod(string name, string? returnType, IEnumerable transitions, IEnumerable args, IEnumerable genericArgs) - { - Name = name; - ReturnType = returnType; - Arguments = [..args]; - CanTransitionTo = [..transitions]; - GenericArguments = [..genericArgs]; - } + public FluidApiMethod(string name, string? returnType, IEnumerable transitions, IEnumerable args, + IEnumerable genericArgs) + { + Name = name; + ReturnType = returnType; + CanTransitionTo = [..transitions]; + GenericArguments = [..genericArgs]; - internal string Name { get; init; } + // We need to order the arguments so that the ones with defaults are last + FluidApiArgument[] enumeratedArgs = args as FluidApiArgument[] ?? args.ToArray(); + var orderedArgs = enumeratedArgs.Where(a => a.DefaultValue is null).Concat(enumeratedArgs.Where(a => a.DefaultValue is not null)).ToList(); + Arguments = [..orderedArgs]; + } - internal string? ReturnType { get; init; } - internal HashSet CanTransitionTo { get; init; } = new(); + internal string Name { get; init; } - internal HashSet Arguments { get; init; } = new(); - - internal HashSet GenericArguments { get; init; } = new(); -} + internal string? ReturnType { get; init; } + internal HashSet CanTransitionTo { get; init; } = new(); + internal HashSet Arguments { get; init; } = new(); + + internal HashSet GenericArguments { get; init; } = new(); +} \ No newline at end of file diff --git a/src/SuperFluid/Internal/Parsers/FluidApiDefinitionParser.cs b/src/SuperFluid/Internal/Parsers/FluidApiDefinitionParser.cs index 1dd75b5..38c6ab5 100644 --- a/src/SuperFluid/Internal/Parsers/FluidApiDefinitionParser.cs +++ b/src/SuperFluid/Internal/Parsers/FluidApiDefinitionParser.cs @@ -85,7 +85,7 @@ private FluidApiMethod FindOrCreateMethod(FluidApiDefinition definition, FluidAp return state; } - List args = method.Arguments.Select(a => new FluidApiArgument(a.Name, a.Type)).ToList(); + List args = method.Arguments.Select(a => new FluidApiArgument(a.Name, a.Type, a.DefaultValue)).ToList(); List genericArgs = method.GenericArguments.Select(a => new FluidGenericArgument(a.Name, a.Constraints)).ToList(); diff --git a/src/SuperFluid/Internal/Services/FluidGeneratorService.cs b/src/SuperFluid/Internal/Services/FluidGeneratorService.cs index ed2f077..b12fb18 100644 --- a/src/SuperFluid/Internal/Services/FluidGeneratorService.cs +++ b/src/SuperFluid/Internal/Services/FluidGeneratorService.cs @@ -44,18 +44,7 @@ public interface {{model.Name}}: {{string.Join(",", model.States.Select(s => s.N private string GenerateStateSource(FluidApiState fluidApiState, FluidApiModel model) { - IEnumerable methodDeclarations = fluidApiState.MethodTransitions.Select(kvp - => - { - string genericArgs = kvp.Key.GenericArguments.Count > 0 ? $"<{string.Join(",", kvp.Key.GenericArguments.Select(a => $"{a.Name}"))}>" : string.Empty; - - string constraints = kvp.Key.GenericArguments.Count > 0 ? $" {string.Join(" ", kvp.Key.GenericArguments.Select(a => $"where {a.Name} : {string.Join(", ", a.Constraints)}"))}" : string.Empty; - - - return $""" - public {kvp.Key.ReturnType ?? kvp.Value.Name} {kvp.Key.Name}{genericArgs}({string.Join(", ", kvp.Key.Arguments.Select(a => $"{a.Type} {a.Name}"))}){constraints}; - """; - }); + IEnumerable methodDeclarations = fluidApiState.MethodTransitions.Select((kvp) => GenerateMethodSource(kvp.Key, kvp.Value)); string source = $$""" namespace {{model.Namespace}}; @@ -69,5 +58,24 @@ public interface {{fluidApiState.Name}} return source; } + private string GenerateMethodSource(FluidApiMethod method, FluidApiState state) + { + string genericArgs = method.GenericArguments.Count > 0 ? $"<{string.Join(",", method.GenericArguments.Select(a => $"{a.Name}"))}>" : string.Empty; + + string constraints = method.GenericArguments.Count > 0 ? $" {string.Join(" ", method.GenericArguments.Select(GenerateGenericConstraintSource))}" : string.Empty; + return $""" + public {method.ReturnType ?? state.Name} {method.Name}{genericArgs}({string.Join(", ", method.Arguments.Select(GenerateMethodArgsSource))}){constraints}; + """; + } + + private string GenerateMethodArgsSource(FluidApiArgument a) + { + return $"{a.Type} {a.Name}{(a.DefaultValue is not null ? " = " + a.DefaultValue : string.Empty)}"; + } + + private string GenerateGenericConstraintSource(FluidGenericArgument a) + { + return $"where {a.Name} : {string.Join(", ", a.Constraints)}"; + } }