From 3525cff36b5f0a8ecbcd0596803099df68fb95ee Mon Sep 17 00:00:00 2001 From: Sander van 't Einde Date: Fri, 2 Aug 2024 19:42:10 +0200 Subject: [PATCH 1/9] Orleans support --- src/Vogen.SharedTypes/Conversions.cs | 11 +- src/Vogen/Generators/ClassGenerator.cs | 1 + .../Conversions/GenerateOrleansConversions.cs | 63 ++ .../IGenerateAssemblyAttributes.cs | 8 + src/Vogen/Generators/RecordClassGenerator.cs | 1 + src/Vogen/Generators/RecordStructGenerator.cs | 1 + src/Vogen/Generators/StructGenerator.cs | 1 + src/Vogen/Util.cs | 37 +- .../OrleansGenerationTests.cs | 33 + .../OrleansGenerationTests.Do.verified.txt | 958 ++++++++++++++++++ 10 files changed, 1102 insertions(+), 12 deletions(-) create mode 100644 src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs create mode 100644 src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs create mode 100644 tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt diff --git a/src/Vogen.SharedTypes/Conversions.cs b/src/Vogen.SharedTypes/Conversions.cs index 191e6ba382..74cfc8e461 100644 --- a/src/Vogen.SharedTypes/Conversions.cs +++ b/src/Vogen.SharedTypes/Conversions.cs @@ -51,14 +51,19 @@ public enum Conversions /// Creates a LinqToDb ValueConverter for converting to and from the type /// LinqToDbValueConverter = 1 << 6, - + /// /// Sets the SerializeFn and DeSerializeFn members in JsConfig in a static constructor. /// ServiceStackDotText = 1 << 7, - + /// /// Creates a BSON serializer for each value object. /// - Bson = 1 << 8 + Bson = 1 << 8, + + /// + /// Creates and registers a codec and copier for Microsoft Orleans + /// + Orleans = 1 << 9 } \ No newline at end of file diff --git a/src/Vogen/Generators/ClassGenerator.cs b/src/Vogen/Generators/ClassGenerator.cs index e7ecfa3b48..2087cae1ab 100644 --- a/src/Vogen/Generators/ClassGenerator.cs +++ b/src/Vogen/Generators/ClassGenerator.cs @@ -14,6 +14,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs new file mode 100644 index 0000000000..7c6eccb9f4 --- /dev/null +++ b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs @@ -0,0 +1,63 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Vogen.Generators.Conversions; + +public class GenerateOrleansConversions : IGenerateConversion, IGenerateAssemblyAttributes +{ + public string GenerateAnyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) => string.Empty; + + public string GenerateAnyBody(TypeDeclarationSyntax tds, VoWorkItem item) + { + if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) + { + return string.Empty; + } + + return $$""" + internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec<{{item.VoTypeName}}> + { + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, {{item.VoTypeName}} value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof({{item.UnderlyingTypeFullName}}), value.Value); + } + + public {{item.VoTypeName}} ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return {{item.VoTypeName}}.From(baseValue); + } + } + + internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier<{{item.VoTypeName}}> + { + public {{item.VoTypeName}} DeepCopy({{item.VoTypeName}} input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return {{item.VoTypeName}}.From(input.Value); + } + } + + internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase + { + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(OrleansSerializer)); + config.Copiers.Add(typeof(OrleansCopier)); + } + } + + """; + } + + public string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) + { + if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) + { + return string.Empty; + } + + return $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({item.FullNamespace}.{item.VoTypeName}.OrleansProvider))]"; + } +} \ No newline at end of file diff --git a/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs b/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs new file mode 100644 index 0000000000..2e71ddbc87 --- /dev/null +++ b/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs @@ -0,0 +1,8 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Vogen.Generators.Conversions; + +public interface IGenerateAssemblyAttributes +{ + string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item); +} \ No newline at end of file diff --git a/src/Vogen/Generators/RecordClassGenerator.cs b/src/Vogen/Generators/RecordClassGenerator.cs index 863fff1b81..c9afdcc787 100644 --- a/src/Vogen/Generators/RecordClassGenerator.cs +++ b/src/Vogen/Generators/RecordClassGenerator.cs @@ -13,6 +13,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/RecordStructGenerator.cs b/src/Vogen/Generators/RecordStructGenerator.cs index 5df709c51d..c213efe529 100644 --- a/src/Vogen/Generators/RecordStructGenerator.cs +++ b/src/Vogen/Generators/RecordStructGenerator.cs @@ -13,6 +13,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/StructGenerator.cs b/src/Vogen/Generators/StructGenerator.cs index 59ccc4242c..d42c275930 100644 --- a/src/Vogen/Generators/StructGenerator.cs +++ b/src/Vogen/Generators/StructGenerator.cs @@ -13,6 +13,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Util.cs b/src/Vogen/Util.cs index b15863536f..a25b2d9cb9 100644 --- a/src/Vogen/Util.cs +++ b/src/Vogen/Util.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Runtime.CompilerServices; using System.Text; using Microsoft.CodeAnalysis; @@ -22,8 +23,9 @@ public static class Util new GenerateDapperConversions(), new GenerateEfCoreTypeConversions(), new GenerateLinqToDbConversions(), + new GenerateOrleansConversions(), }; - + public static string SanitizeToALegalFilename(string input) => input.Replace('@', '_'); public static void TryWriteUsingUniqueFilename(string filename, SourceProductionContext context, SourceText sourceText) @@ -49,7 +51,7 @@ public static void TryWriteUsingUniqueFilename(string filename, SourceProduction } } } - + public static string GenerateCallToValidationAndThrowIfRequired(VoWorkItem workItem) @@ -90,7 +92,7 @@ public static string GenerateCallToValidationAndReturnFalseIfNeeded(VoWorkItem w return string.Empty; } - + public static string GenerateNotNullWhenTrueAttribute() => """ @@ -213,6 +215,23 @@ public static string GenerateAnyConversionAttributes(TypeDeclarationSyntax tds, return sb.ToString(); } + public static string GenerateAssemblyConversionAttributes(TypeDeclarationSyntax tds, VoWorkItem item) + { + StringBuilder sb = new StringBuilder(); + + foreach (var conversionGenerator in _conversionGenerators.OfType()) + { + var attribute = conversionGenerator.GenerateAssemblyAttributes(tds, item); + + if (!string.IsNullOrEmpty(attribute)) + { + sb.Append(attribute); + } + } + + return sb.ToString(); + } + private static string GenerateAnyConversionAttributesForDebuggerProxy(VoWorkItem item) => item.Config.Conversions.ToString(); public static string GenerateAnyConversionBodies(TypeDeclarationSyntax tds, VoWorkItem item) @@ -231,8 +250,8 @@ public static string GenerateDebuggerProxyForStructs(VoWorkItem item) var createdWithMethod = item.Config.DisableStackTraceRecordingInDebug ? @"public global::System.String CreatedWith => ""the From method""" : @"public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? ""the From method"""; - - string code = + + string code = $$""" internal sealed class {{item.VoTypeName}}DebugView @@ -289,7 +308,7 @@ public static string GenerateDebuggerProxyForClasses(TypeDeclarationSyntax tds, private static string GenerateToString(VoWorkItem item, bool isReadOnly) { string ro = isReadOnly ? " readonly" : string.Empty; - + return item.UserProvidedOverloads.ToStringInfo.WasSupplied ? string.Empty : $@"/// Returns the string representation of the underlying . @@ -339,7 +358,7 @@ causes Rider's debugger to crash. */"; } - + return source; } @@ -349,7 +368,7 @@ public static string GenerateStackTraceFieldIfNeeded(VoWorkItem item) { return string.Empty; } - + return $""" #if DEBUG private readonly global::System.Diagnostics.StackTrace _stackTrace = null; @@ -367,7 +386,7 @@ public static string GenerateMessageForUninitializedValueObject(VoWorkItem item) if (item.Config.DisableStackTraceRecordingInDebug) { return $"""global::System.String message = "Use of uninitialized Value Object.";"""; - + } return $"""global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? "";"""; } diff --git a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs new file mode 100644 index 0000000000..a3039c7d66 --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Shared; +using Vogen; + +namespace SnapshotTests.OrleansGeneration; + +public class OrleansGenerationTests +{ + [Fact] + public async Task Do() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.Orleans)] + + namespace OrleansTests; + + [ValueObject] + public partial struct Age; + + [ValueObject] + public partial struct Name; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } +} \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt new file mode 100644 index 0000000000..c1e147a225 --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt @@ -0,0 +1,958 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +namespace generator; + +public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory +{ + public VogenTypesFactory() { } + private static readonly global::System.Collections.Generic.Dictionary> _lookup = + new global::System.Collections.Generic.Dictionary> { + + }; + + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => + _lookup[typeToConvert].Value; +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.Age.OrleansProvider))] + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(AgeDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.Int32, Value = { _value }")] + public partial struct Age : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.ISpanParsable, global::System.IUtf8SpanParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.Int32 _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.Int32 Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Age() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Age(System.Int32 value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Age From(System.Int32 value) + { + + + + + Age instance = new Age(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.Int32 value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age vo) +{ + + + + + + vo = new Age(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.Int32 value) +{ + + + + + + + return new ValueObjectOrError(new Age(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Age(System.Int32 value) => From(value); + public static explicit operator System.Int32(Age value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Age __Deserialize(System.Int32 value) + { + + + + + return new Age(value); + } + public readonly global::System.Boolean Equals(Age other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Age other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.Int32 primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Age && Equals((Age) obj); + } + + public static global::System.Boolean operator ==(Age left, Age right) => Equals(left, right); + public static global::System.Boolean operator !=(Age left, Age right) => !(left == right); + + public static global::System.Boolean operator ==(Age left, System.Int32 right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Age left, System.Int32 right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.Int32 left, Age right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.Int32 left, Age right) => !Equals(left, right.Value); + + public int CompareTo(Age other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Age x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Age", nameof(other)); + } + + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s) { + var r = System.Int32.Parse(s); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style) { + var r = System.Int32.Parse(s, style); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + +internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Age value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.Int32), value.Value); + } + + public Age ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return Age.From(baseValue); + } +} + +internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Age DeepCopy(Age input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Age.From(input.Value); + } +} + +internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(OrleansSerializer)); + config.Copiers.Add(typeof(OrleansCopier)); + } +} + + + + + internal sealed class AgeDebugView + { + private readonly Age _t; + + AgeDebugView(Age t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Int32"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.Name.OrleansProvider))] + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(NameDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.String, Value = { _value }")] + public partial struct Name : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.String _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.String Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Name() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Name(System.String value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Name From(System.String value) + { + + + + + Name instance = new Name(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.String value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name vo) +{ + + + + + + vo = new Name(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.String value) +{ + + + + + + + return new ValueObjectOrError(new Name(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Name(System.String value) => From(value); + public static explicit operator System.String(Name value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Name __Deserialize(System.String value) + { + + + + + return new Name(value); + } + public readonly global::System.Boolean Equals(Name other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Name other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.String primitive) + { + return Value.Equals(primitive); + } + + public readonly global::System.Boolean Equals(System.String primitive, global::System.StringComparer comparer) + { + return comparer.Equals(Value, primitive); + } + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Name && Equals((Name) obj); + } + + public static global::System.Boolean operator ==(Name left, Name right) => Equals(left, right); + public static global::System.Boolean operator !=(Name left, Name right) => !(left == right); + + public static global::System.Boolean operator ==(Name left, System.String right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Name left, System.String right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.String left, Name right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.String left, Name right) => !Equals(left, right.Value); + + public int CompareTo(Name other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Name x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Name", nameof(other)); + } + + + /// + /// + /// + /// True if the value passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.String s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name result) { + + + result = new Name(s); + return true; + } + /// + /// + /// + /// The value created via the method. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Name Parse(global::System.String s, global::System.IFormatProvider provider) { + return From(s); + } + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + +internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Name value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.String), value.Value); + } + + public Name ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return Name.From(baseValue); + } +} + +internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Name DeepCopy(Name input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Name.From(input.Value); + } +} + +internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(OrleansSerializer)); + config.Copiers.Add(typeof(OrleansCopier)); + } +} + + + + + internal sealed class NameDebugView + { + private readonly Name _t; + + NameDebugView(Name t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.String"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} +] \ No newline at end of file From 5e503bd692913e7519a90428fad401d4ce461f56 Mon Sep 17 00:00:00 2001 From: Sander van 't Einde Date: Fri, 2 Aug 2024 19:53:05 +0200 Subject: [PATCH 2/9] Ensure also generated when not in a namespace --- .../Generators/Conversions/GenerateOrleansConversions.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs index 7c6eccb9f4..0ac2791755 100644 --- a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs +++ b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs @@ -58,6 +58,10 @@ public string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem i return string.Empty; } - return $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({item.FullNamespace}.{item.VoTypeName}.OrleansProvider))]"; + var voNamespace = item.FullNamespace; + + return string.IsNullOrWhiteSpace(voNamespace) + ? $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider({item.VoTypeName}.OrleansProvider))]" + : $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({voNamespace}.{item.VoTypeName}.OrleansProvider))]"; } } \ No newline at end of file From e26257e76d701433ab86d1dec000a377845ee314 Mon Sep 17 00:00:00 2001 From: Steve Dunn Date: Mon, 5 Aug 2024 22:14:18 +0100 Subject: [PATCH 3/9] First pass at moving away from inner class implementation --- src/Vogen.SharedTypes/Conversions.cs | 3 +- src/Vogen/BuildConfigurationFromAttributes.cs | 4 +- src/Vogen/Generators/ClassGenerator.cs | 1 - .../Conversions/GenerateOrleansConversions.cs | 67 ------------ .../IGenerateAssemblyAttributes.cs | 8 -- src/Vogen/Generators/RecordClassGenerator.cs | 1 - src/Vogen/Generators/RecordStructGenerator.cs | 1 - src/Vogen/Generators/StructGenerator.cs | 1 - src/Vogen/Util.cs | 20 +--- src/Vogen/ValueObjectGenerator.cs | 2 + src/Vogen/WriteOrleansSerializers.cs | 102 ++++++++++++++++++ tests/AnalyzerTests/GlobalConfig/SadTests.cs | 4 +- tests/AnalyzerTests/LocalConfig/SadTests.cs | 2 +- .../OrleansGenerationTests.cs | 101 ++++++++++++++++- ...Skipped_on_charp_less_than_12.verified.txt | 1 + ...erationTests.Skipped_on_net_7.verified.txt | 1 + ...ionTests.Escapes_wrapper_name.verified.txt | 1 + ...e_set_and_on_net_8_or_greater.verified.txt | 1 + ...accessibility_of_value_object.verified.txt | 1 + 19 files changed, 217 insertions(+), 105 deletions(-) delete mode 100644 src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs delete mode 100644 src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs create mode 100644 src/Vogen/WriteOrleansSerializers.cs create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt diff --git a/src/Vogen.SharedTypes/Conversions.cs b/src/Vogen.SharedTypes/Conversions.cs index 74cfc8e461..b8bff9f59f 100644 --- a/src/Vogen.SharedTypes/Conversions.cs +++ b/src/Vogen.SharedTypes/Conversions.cs @@ -63,7 +63,8 @@ public enum Conversions Bson = 1 << 8, /// - /// Creates and registers a codec and copier for Microsoft Orleans + /// Creates and registers a codec and copier for Microsoft Orleans. + /// This feature requires .NET 8 and C#12 and cannot be polly-filled. /// Orleans = 1 << 9 } \ No newline at end of file diff --git a/src/Vogen/BuildConfigurationFromAttributes.cs b/src/Vogen/BuildConfigurationFromAttributes.cs index d520a63031..16c57c5bb8 100644 --- a/src/Vogen/BuildConfigurationFromAttributes.cs +++ b/src/Vogen/BuildConfigurationFromAttributes.cs @@ -70,10 +70,10 @@ private BuildConfigurationFromAttributes(AttributeData att) } public static VogenConfigurationBuildResult TryBuildFromValueObjectAttribute(AttributeData matchingAttribute) => - new BuildConfigurationFromAttributes(matchingAttribute).Build(false); + new BuildConfigurationFromAttributes(matchingAttribute).Build(argsAreFromVogenDefaultAttribute: false); public static VogenConfigurationBuildResult TryBuildFromVogenDefaultsAttribute(AttributeData matchingAttribute) => - new BuildConfigurationFromAttributes(matchingAttribute).Build(true); + new BuildConfigurationFromAttributes(matchingAttribute).Build(argsAreFromVogenDefaultAttribute: true); private VogenConfigurationBuildResult Build(bool argsAreFromVogenDefaultAttribute) { diff --git a/src/Vogen/Generators/ClassGenerator.cs b/src/Vogen/Generators/ClassGenerator.cs index 2087cae1ab..e7ecfa3b48 100644 --- a/src/Vogen/Generators/ClassGenerator.cs +++ b/src/Vogen/Generators/ClassGenerator.cs @@ -14,7 +14,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs deleted file mode 100644 index 0ac2791755..0000000000 --- a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Vogen.Generators.Conversions; - -public class GenerateOrleansConversions : IGenerateConversion, IGenerateAssemblyAttributes -{ - public string GenerateAnyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) => string.Empty; - - public string GenerateAnyBody(TypeDeclarationSyntax tds, VoWorkItem item) - { - if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) - { - return string.Empty; - } - - return $$""" - internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec<{{item.VoTypeName}}> - { - public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, {{item.VoTypeName}} value) - where TBufferWriter : global::System.Buffers.IBufferWriter - { - var baseCodec = writer.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); - baseCodec.WriteField(ref writer, fieldIdDelta, typeof({{item.UnderlyingTypeFullName}}), value.Value); - } - - public {{item.VoTypeName}} ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) - { - var baseCodec = reader.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); - var baseValue = baseCodec.ReadValue(ref reader, field); - return {{item.VoTypeName}}.From(baseValue); - } - } - - internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier<{{item.VoTypeName}}> - { - public {{item.VoTypeName}} DeepCopy({{item.VoTypeName}} input, global::Orleans.Serialization.Cloning.CopyContext context) - { - return {{item.VoTypeName}}.From(input.Value); - } - } - - internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase - { - protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) - { - config.Serializers.Add(typeof(OrleansSerializer)); - config.Copiers.Add(typeof(OrleansCopier)); - } - } - - """; - } - - public string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) - { - if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) - { - return string.Empty; - } - - var voNamespace = item.FullNamespace; - - return string.IsNullOrWhiteSpace(voNamespace) - ? $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider({item.VoTypeName}.OrleansProvider))]" - : $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({voNamespace}.{item.VoTypeName}.OrleansProvider))]"; - } -} \ No newline at end of file diff --git a/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs b/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs deleted file mode 100644 index 2e71ddbc87..0000000000 --- a/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Vogen.Generators.Conversions; - -public interface IGenerateAssemblyAttributes -{ - string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item); -} \ No newline at end of file diff --git a/src/Vogen/Generators/RecordClassGenerator.cs b/src/Vogen/Generators/RecordClassGenerator.cs index c9afdcc787..863fff1b81 100644 --- a/src/Vogen/Generators/RecordClassGenerator.cs +++ b/src/Vogen/Generators/RecordClassGenerator.cs @@ -13,7 +13,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/RecordStructGenerator.cs b/src/Vogen/Generators/RecordStructGenerator.cs index c213efe529..5df709c51d 100644 --- a/src/Vogen/Generators/RecordStructGenerator.cs +++ b/src/Vogen/Generators/RecordStructGenerator.cs @@ -13,7 +13,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/StructGenerator.cs b/src/Vogen/Generators/StructGenerator.cs index d42c275930..59ccc4242c 100644 --- a/src/Vogen/Generators/StructGenerator.cs +++ b/src/Vogen/Generators/StructGenerator.cs @@ -13,7 +13,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Util.cs b/src/Vogen/Util.cs index a25b2d9cb9..bc489beaff 100644 --- a/src/Vogen/Util.cs +++ b/src/Vogen/Util.cs @@ -22,8 +22,7 @@ public static class Util new GenerateTypeConverterConversions(), new GenerateDapperConversions(), new GenerateEfCoreTypeConversions(), - new GenerateLinqToDbConversions(), - new GenerateOrleansConversions(), + new GenerateLinqToDbConversions() }; public static string SanitizeToALegalFilename(string input) => input.Replace('@', '_'); @@ -215,23 +214,6 @@ public static string GenerateAnyConversionAttributes(TypeDeclarationSyntax tds, return sb.ToString(); } - public static string GenerateAssemblyConversionAttributes(TypeDeclarationSyntax tds, VoWorkItem item) - { - StringBuilder sb = new StringBuilder(); - - foreach (var conversionGenerator in _conversionGenerators.OfType()) - { - var attribute = conversionGenerator.GenerateAssemblyAttributes(tds, item); - - if (!string.IsNullOrEmpty(attribute)) - { - sb.Append(attribute); - } - } - - return sb.ToString(); - } - private static string GenerateAnyConversionAttributesForDebuggerProxy(VoWorkItem item) => item.Config.Conversions.ToString(); public static string GenerateAnyConversionBodies(TypeDeclarationSyntax tds, VoWorkItem item) diff --git a/src/Vogen/ValueObjectGenerator.cs b/src/Vogen/ValueObjectGenerator.cs index 5fd49a737b..69ae3c947b 100644 --- a/src/Vogen/ValueObjectGenerator.cs +++ b/src/Vogen/ValueObjectGenerator.cs @@ -99,6 +99,8 @@ private static void Execute( WriteEfCoreSpecs.WriteIfNeeded(spc, compilation, efCoreConverterSpecs); WriteBsonSerializers.WriteIfNeeded(spc, compilation, workItems); + + WriteOrleansSerializers.WriteIfNeeded(spc, workItems); if (workItems.Count > 0) { diff --git a/src/Vogen/WriteOrleansSerializers.cs b/src/Vogen/WriteOrleansSerializers.cs new file mode 100644 index 0000000000..6646fdb8df --- /dev/null +++ b/src/Vogen/WriteOrleansSerializers.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Vogen; + +internal class WriteOrleansSerializers +{ + public static void WriteIfNeeded(SourceProductionContext context, List workItems) + { + var items = workItems.Where(i => i.Config.Conversions.HasFlag(Conversions.Orleans)).ToList(); + + var toWrite = items.Select(GenerateSource).ToList(); + + foreach (var eachEntry in toWrite) + { + WriteSerializer(eachEntry, context); + } + } + + private static void WriteSerializer(SerializerEntry entry, SourceProductionContext context) + { + SourceText sourceText = SourceText.From(entry.SourceCode, Encoding.UTF8); + + Util.TryWriteUsingUniqueFilename(entry.Filename, context, sourceText); + } + + public record SerializerEntry(string Filename, string SourceCode); + + private static SerializerEntry GenerateSource(VoWorkItem item) + { + var fullNamespace = item.FullNamespace; + + var isPublic = item.WrapperType.DeclaredAccessibility.HasFlag(Accessibility.Public); + var accessor = isPublic ? "public" : "internal"; + + var ns = string.IsNullOrEmpty(fullNamespace) ? string.Empty : $"namespace {fullNamespace};"; + + string unescapedWrapperName = item.WrapperType.Name; + string wrapperName = Util.EscapeIfRequired(unescapedWrapperName); + string underlyingTypeName = item.UnderlyingTypeFullName; + + var assemblyAttribute = string.IsNullOrWhiteSpace(fullNamespace) + ? $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider({item.VoTypeName}OrleansProvider))]" + : $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({fullNamespace}.{item.VoTypeName}OrleansProvider))]"; + + + string sb = + $$""" + {{GeneratedCodeSegments.Preamble}} + + {{assemblyAttribute}} + + {{ns}} + + {{accessor}} sealed class {{unescapedWrapperName}}OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec<{{item.VoTypeName}}> + { + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, {{item.VoTypeName}} value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec<{{underlyingTypeName}}>(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof({{underlyingTypeName}}), value.Value); + } + + public {{item.VoTypeName}} ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec<{{underlyingTypeName}}>(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern {{wrapperName}} UnsafeDeserialize({{wrapperName}} @this, {{underlyingTypeName}} value); + + } + + {{accessor}} sealed class {{unescapedWrapperName}}OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier<{{item.VoTypeName}}> + { + public {{item.VoTypeName}} DeepCopy({{item.VoTypeName}} input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return {{item.VoTypeName}}.From(input.Value); + } + } + + {{accessor}} class {{unescapedWrapperName}}OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase + { + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof({{unescapedWrapperName}}OrleansSerializer)); + config.Copiers.Add(typeof({{unescapedWrapperName}}OrleansCopier)); + } + } + + """; + + var unsanitized = $"{item.WrapperType.ToDisplayString()}_orleans.g.cs"; + string filename = Util.SanitizeToALegalFilename(unsanitized); + return new SerializerEntry(filename, sb); + } +} \ No newline at end of file diff --git a/tests/AnalyzerTests/GlobalConfig/SadTests.cs b/tests/AnalyzerTests/GlobalConfig/SadTests.cs index 4a5351d6a3..466d51e5dc 100644 --- a/tests/AnalyzerTests/GlobalConfig/SadTests.cs +++ b/tests/AnalyzerTests/GlobalConfig/SadTests.cs @@ -178,7 +178,7 @@ public async Task Not_valid_conversion() var source = @"using System; using Vogen; -[assembly: VogenDefaults(conversions: (Conversions)666)] +[assembly: VogenDefaults(conversions: (Conversions)4242)] namespace Whatever; @@ -279,7 +279,7 @@ public async Task Not_valid_customization_or_conversion() var source = @"using System; using Vogen; -[assembly: VogenDefaults(customizations: (Customizations)666, conversions: (Conversions)666)] +[assembly: VogenDefaults(customizations: (Customizations)666, conversions: (Conversions)4242)] namespace Whatever; diff --git a/tests/AnalyzerTests/LocalConfig/SadTests.cs b/tests/AnalyzerTests/LocalConfig/SadTests.cs index 44c85548d8..ba9bb5a660 100644 --- a/tests/AnalyzerTests/LocalConfig/SadTests.cs +++ b/tests/AnalyzerTests/LocalConfig/SadTests.cs @@ -193,7 +193,7 @@ public async Task Not_valid_conversion(string type) namespace Whatever; -[ValueObject(conversions: (Conversions)666)] +[ValueObject(conversions: (Conversions)4242)] public {type} CustomerId {{ }} "; diff --git a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs index a3039c7d66..00d6ba3e05 100644 --- a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs +++ b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; using Shared; using Vogen; @@ -7,7 +8,7 @@ namespace SnapshotTests.OrleansGeneration; public class OrleansGenerationTests { [Fact] - public async Task Do() + public async Task Generates_if_global_attribute_set_and_on_net_8_or_greater() { var source = """ using System; @@ -30,4 +31,102 @@ public partial struct Name; .IgnoreInitialCompilationErrors() .RunOn(TargetFramework.Net8_0); } + + [Fact] + public async Task Generates_if_local_attribute_set_and_on_net_8_or_greater() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace OrleansTests; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Age; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Name; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } + + [Fact] + public async Task Takes_on_accessibility_of_value_object() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace OrleansTests; + + [ValueObject(conversions: Conversions.Orleans)] + internal partial struct Age; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Name; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } + + [Fact] + public async Task Escapes_wrapper_name() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace @class.@struct.@record; + + [ValueObject(conversions: Conversions.Orleans)] + internal partial struct @class; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } + + [Fact] + public async Task Skipped_on_csharp_less_than_12() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace OrleansTests; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Age { } + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Name { } + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .WithLanguageVersion(LanguageVersion.CSharp11) + .RunOn(TargetFramework.Net7_0); + } } \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file From c4ebfb6b0581a4e9629d16b23eb9429761fcfb67 Mon Sep 17 00:00:00 2001 From: Sander van 't Einde Date: Fri, 2 Aug 2024 19:42:10 +0200 Subject: [PATCH 4/9] Orleans support --- src/Vogen.SharedTypes/Conversions.cs | 11 +- src/Vogen/Generators/ClassGenerator.cs | 1 + .../Conversions/GenerateOrleansConversions.cs | 63 ++ .../IGenerateAssemblyAttributes.cs | 8 + src/Vogen/Generators/RecordClassGenerator.cs | 1 + src/Vogen/Generators/RecordStructGenerator.cs | 1 + src/Vogen/Generators/StructGenerator.cs | 1 + src/Vogen/Util.cs | 37 +- .../OrleansGenerationTests.cs | 33 + .../OrleansGenerationTests.Do.verified.txt | 958 ++++++++++++++++++ 10 files changed, 1102 insertions(+), 12 deletions(-) create mode 100644 src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs create mode 100644 src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs create mode 100644 tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt diff --git a/src/Vogen.SharedTypes/Conversions.cs b/src/Vogen.SharedTypes/Conversions.cs index 191e6ba382..74cfc8e461 100644 --- a/src/Vogen.SharedTypes/Conversions.cs +++ b/src/Vogen.SharedTypes/Conversions.cs @@ -51,14 +51,19 @@ public enum Conversions /// Creates a LinqToDb ValueConverter for converting to and from the type /// LinqToDbValueConverter = 1 << 6, - + /// /// Sets the SerializeFn and DeSerializeFn members in JsConfig in a static constructor. /// ServiceStackDotText = 1 << 7, - + /// /// Creates a BSON serializer for each value object. /// - Bson = 1 << 8 + Bson = 1 << 8, + + /// + /// Creates and registers a codec and copier for Microsoft Orleans + /// + Orleans = 1 << 9 } \ No newline at end of file diff --git a/src/Vogen/Generators/ClassGenerator.cs b/src/Vogen/Generators/ClassGenerator.cs index e7ecfa3b48..2087cae1ab 100644 --- a/src/Vogen/Generators/ClassGenerator.cs +++ b/src/Vogen/Generators/ClassGenerator.cs @@ -14,6 +14,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs new file mode 100644 index 0000000000..7c6eccb9f4 --- /dev/null +++ b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs @@ -0,0 +1,63 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Vogen.Generators.Conversions; + +public class GenerateOrleansConversions : IGenerateConversion, IGenerateAssemblyAttributes +{ + public string GenerateAnyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) => string.Empty; + + public string GenerateAnyBody(TypeDeclarationSyntax tds, VoWorkItem item) + { + if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) + { + return string.Empty; + } + + return $$""" + internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec<{{item.VoTypeName}}> + { + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, {{item.VoTypeName}} value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof({{item.UnderlyingTypeFullName}}), value.Value); + } + + public {{item.VoTypeName}} ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return {{item.VoTypeName}}.From(baseValue); + } + } + + internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier<{{item.VoTypeName}}> + { + public {{item.VoTypeName}} DeepCopy({{item.VoTypeName}} input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return {{item.VoTypeName}}.From(input.Value); + } + } + + internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase + { + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(OrleansSerializer)); + config.Copiers.Add(typeof(OrleansCopier)); + } + } + + """; + } + + public string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) + { + if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) + { + return string.Empty; + } + + return $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({item.FullNamespace}.{item.VoTypeName}.OrleansProvider))]"; + } +} \ No newline at end of file diff --git a/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs b/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs new file mode 100644 index 0000000000..2e71ddbc87 --- /dev/null +++ b/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs @@ -0,0 +1,8 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Vogen.Generators.Conversions; + +public interface IGenerateAssemblyAttributes +{ + string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item); +} \ No newline at end of file diff --git a/src/Vogen/Generators/RecordClassGenerator.cs b/src/Vogen/Generators/RecordClassGenerator.cs index 863fff1b81..c9afdcc787 100644 --- a/src/Vogen/Generators/RecordClassGenerator.cs +++ b/src/Vogen/Generators/RecordClassGenerator.cs @@ -13,6 +13,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/RecordStructGenerator.cs b/src/Vogen/Generators/RecordStructGenerator.cs index 5df709c51d..c213efe529 100644 --- a/src/Vogen/Generators/RecordStructGenerator.cs +++ b/src/Vogen/Generators/RecordStructGenerator.cs @@ -13,6 +13,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/StructGenerator.cs b/src/Vogen/Generators/StructGenerator.cs index 59ccc4242c..d42c275930 100644 --- a/src/Vogen/Generators/StructGenerator.cs +++ b/src/Vogen/Generators/StructGenerator.cs @@ -13,6 +13,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; +{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Util.cs b/src/Vogen/Util.cs index b15863536f..a25b2d9cb9 100644 --- a/src/Vogen/Util.cs +++ b/src/Vogen/Util.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Runtime.CompilerServices; using System.Text; using Microsoft.CodeAnalysis; @@ -22,8 +23,9 @@ public static class Util new GenerateDapperConversions(), new GenerateEfCoreTypeConversions(), new GenerateLinqToDbConversions(), + new GenerateOrleansConversions(), }; - + public static string SanitizeToALegalFilename(string input) => input.Replace('@', '_'); public static void TryWriteUsingUniqueFilename(string filename, SourceProductionContext context, SourceText sourceText) @@ -49,7 +51,7 @@ public static void TryWriteUsingUniqueFilename(string filename, SourceProduction } } } - + public static string GenerateCallToValidationAndThrowIfRequired(VoWorkItem workItem) @@ -90,7 +92,7 @@ public static string GenerateCallToValidationAndReturnFalseIfNeeded(VoWorkItem w return string.Empty; } - + public static string GenerateNotNullWhenTrueAttribute() => """ @@ -213,6 +215,23 @@ public static string GenerateAnyConversionAttributes(TypeDeclarationSyntax tds, return sb.ToString(); } + public static string GenerateAssemblyConversionAttributes(TypeDeclarationSyntax tds, VoWorkItem item) + { + StringBuilder sb = new StringBuilder(); + + foreach (var conversionGenerator in _conversionGenerators.OfType()) + { + var attribute = conversionGenerator.GenerateAssemblyAttributes(tds, item); + + if (!string.IsNullOrEmpty(attribute)) + { + sb.Append(attribute); + } + } + + return sb.ToString(); + } + private static string GenerateAnyConversionAttributesForDebuggerProxy(VoWorkItem item) => item.Config.Conversions.ToString(); public static string GenerateAnyConversionBodies(TypeDeclarationSyntax tds, VoWorkItem item) @@ -231,8 +250,8 @@ public static string GenerateDebuggerProxyForStructs(VoWorkItem item) var createdWithMethod = item.Config.DisableStackTraceRecordingInDebug ? @"public global::System.String CreatedWith => ""the From method""" : @"public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? ""the From method"""; - - string code = + + string code = $$""" internal sealed class {{item.VoTypeName}}DebugView @@ -289,7 +308,7 @@ public static string GenerateDebuggerProxyForClasses(TypeDeclarationSyntax tds, private static string GenerateToString(VoWorkItem item, bool isReadOnly) { string ro = isReadOnly ? " readonly" : string.Empty; - + return item.UserProvidedOverloads.ToStringInfo.WasSupplied ? string.Empty : $@"/// Returns the string representation of the underlying . @@ -339,7 +358,7 @@ causes Rider's debugger to crash. */"; } - + return source; } @@ -349,7 +368,7 @@ public static string GenerateStackTraceFieldIfNeeded(VoWorkItem item) { return string.Empty; } - + return $""" #if DEBUG private readonly global::System.Diagnostics.StackTrace _stackTrace = null; @@ -367,7 +386,7 @@ public static string GenerateMessageForUninitializedValueObject(VoWorkItem item) if (item.Config.DisableStackTraceRecordingInDebug) { return $"""global::System.String message = "Use of uninitialized Value Object.";"""; - + } return $"""global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? "";"""; } diff --git a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs new file mode 100644 index 0000000000..a3039c7d66 --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Shared; +using Vogen; + +namespace SnapshotTests.OrleansGeneration; + +public class OrleansGenerationTests +{ + [Fact] + public async Task Do() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.Orleans)] + + namespace OrleansTests; + + [ValueObject] + public partial struct Age; + + [ValueObject] + public partial struct Name; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } +} \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt new file mode 100644 index 0000000000..c1e147a225 --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Do.verified.txt @@ -0,0 +1,958 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +namespace generator; + +public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory +{ + public VogenTypesFactory() { } + private static readonly global::System.Collections.Generic.Dictionary> _lookup = + new global::System.Collections.Generic.Dictionary> { + + }; + + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => + _lookup[typeToConvert].Value; +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.Age.OrleansProvider))] + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(AgeDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.Int32, Value = { _value }")] + public partial struct Age : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.ISpanParsable, global::System.IUtf8SpanParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.Int32 _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.Int32 Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Age() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Age(System.Int32 value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Age From(System.Int32 value) + { + + + + + Age instance = new Age(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.Int32 value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age vo) +{ + + + + + + vo = new Age(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.Int32 value) +{ + + + + + + + return new ValueObjectOrError(new Age(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Age(System.Int32 value) => From(value); + public static explicit operator System.Int32(Age value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Age __Deserialize(System.Int32 value) + { + + + + + return new Age(value); + } + public readonly global::System.Boolean Equals(Age other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Age other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.Int32 primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Age && Equals((Age) obj); + } + + public static global::System.Boolean operator ==(Age left, Age right) => Equals(left, right); + public static global::System.Boolean operator !=(Age left, Age right) => !(left == right); + + public static global::System.Boolean operator ==(Age left, System.Int32 right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Age left, System.Int32 right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.Int32 left, Age right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.Int32 left, Age right) => !Equals(left, right.Value); + + public int CompareTo(Age other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Age x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Age", nameof(other)); + } + + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s) { + var r = System.Int32.Parse(s); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style) { + var r = System.Int32.Parse(s, style); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + +internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Age value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.Int32), value.Value); + } + + public Age ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return Age.From(baseValue); + } +} + +internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Age DeepCopy(Age input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Age.From(input.Value); + } +} + +internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(OrleansSerializer)); + config.Copiers.Add(typeof(OrleansCopier)); + } +} + + + + + internal sealed class AgeDebugView + { + private readonly Age _t; + + AgeDebugView(Age t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Int32"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.Name.OrleansProvider))] + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(NameDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.String, Value = { _value }")] + public partial struct Name : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.String _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.String Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Name() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Name(System.String value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Name From(System.String value) + { + + + + + Name instance = new Name(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.String value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name vo) +{ + + + + + + vo = new Name(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.String value) +{ + + + + + + + return new ValueObjectOrError(new Name(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Name(System.String value) => From(value); + public static explicit operator System.String(Name value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Name __Deserialize(System.String value) + { + + + + + return new Name(value); + } + public readonly global::System.Boolean Equals(Name other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Name other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.String primitive) + { + return Value.Equals(primitive); + } + + public readonly global::System.Boolean Equals(System.String primitive, global::System.StringComparer comparer) + { + return comparer.Equals(Value, primitive); + } + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Name && Equals((Name) obj); + } + + public static global::System.Boolean operator ==(Name left, Name right) => Equals(left, right); + public static global::System.Boolean operator !=(Name left, Name right) => !(left == right); + + public static global::System.Boolean operator ==(Name left, System.String right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Name left, System.String right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.String left, Name right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.String left, Name right) => !Equals(left, right.Value); + + public int CompareTo(Name other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Name x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Name", nameof(other)); + } + + + /// + /// + /// + /// True if the value passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.String s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name result) { + + + result = new Name(s); + return true; + } + /// + /// + /// + /// The value created via the method. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Name Parse(global::System.String s, global::System.IFormatProvider provider) { + return From(s); + } + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + +internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Name value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.String), value.Value); + } + + public Name ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return Name.From(baseValue); + } +} + +internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Name DeepCopy(Name input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Name.From(input.Value); + } +} + +internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(OrleansSerializer)); + config.Copiers.Add(typeof(OrleansCopier)); + } +} + + + + + internal sealed class NameDebugView + { + private readonly Name _t; + + NameDebugView(Name t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.String"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} +] \ No newline at end of file From 2cfcb934287a5dbc266ef634da501fb6049a379b Mon Sep 17 00:00:00 2001 From: Sander van 't Einde Date: Fri, 2 Aug 2024 19:53:05 +0200 Subject: [PATCH 5/9] Ensure also generated when not in a namespace --- .../Generators/Conversions/GenerateOrleansConversions.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs index 7c6eccb9f4..0ac2791755 100644 --- a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs +++ b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs @@ -58,6 +58,10 @@ public string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem i return string.Empty; } - return $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({item.FullNamespace}.{item.VoTypeName}.OrleansProvider))]"; + var voNamespace = item.FullNamespace; + + return string.IsNullOrWhiteSpace(voNamespace) + ? $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider({item.VoTypeName}.OrleansProvider))]" + : $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({voNamespace}.{item.VoTypeName}.OrleansProvider))]"; } } \ No newline at end of file From f68c0a82e566b7fd327146b135272a743ce37a53 Mon Sep 17 00:00:00 2001 From: Steve Dunn Date: Mon, 5 Aug 2024 22:14:18 +0100 Subject: [PATCH 6/9] First pass at moving away from inner class implementation --- src/Vogen.SharedTypes/Conversions.cs | 3 +- src/Vogen/BuildConfigurationFromAttributes.cs | 4 +- src/Vogen/Generators/ClassGenerator.cs | 1 - .../Conversions/GenerateOrleansConversions.cs | 67 ------------ .../IGenerateAssemblyAttributes.cs | 8 -- src/Vogen/Generators/RecordClassGenerator.cs | 1 - src/Vogen/Generators/RecordStructGenerator.cs | 1 - src/Vogen/Generators/StructGenerator.cs | 1 - src/Vogen/Util.cs | 20 +--- src/Vogen/ValueObjectGenerator.cs | 2 + src/Vogen/WriteOrleansSerializers.cs | 102 ++++++++++++++++++ tests/AnalyzerTests/GlobalConfig/SadTests.cs | 4 +- tests/AnalyzerTests/LocalConfig/SadTests.cs | 2 +- .../OrleansGenerationTests.cs | 101 ++++++++++++++++- ...Skipped_on_charp_less_than_12.verified.txt | 1 + ...erationTests.Skipped_on_net_7.verified.txt | 1 + ...ionTests.Escapes_wrapper_name.verified.txt | 1 + ...e_set_and_on_net_8_or_greater.verified.txt | 1 + ...accessibility_of_value_object.verified.txt | 1 + 19 files changed, 217 insertions(+), 105 deletions(-) delete mode 100644 src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs delete mode 100644 src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs create mode 100644 src/Vogen/WriteOrleansSerializers.cs create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt diff --git a/src/Vogen.SharedTypes/Conversions.cs b/src/Vogen.SharedTypes/Conversions.cs index 74cfc8e461..b8bff9f59f 100644 --- a/src/Vogen.SharedTypes/Conversions.cs +++ b/src/Vogen.SharedTypes/Conversions.cs @@ -63,7 +63,8 @@ public enum Conversions Bson = 1 << 8, /// - /// Creates and registers a codec and copier for Microsoft Orleans + /// Creates and registers a codec and copier for Microsoft Orleans. + /// This feature requires .NET 8 and C#12 and cannot be polly-filled. /// Orleans = 1 << 9 } \ No newline at end of file diff --git a/src/Vogen/BuildConfigurationFromAttributes.cs b/src/Vogen/BuildConfigurationFromAttributes.cs index d520a63031..16c57c5bb8 100644 --- a/src/Vogen/BuildConfigurationFromAttributes.cs +++ b/src/Vogen/BuildConfigurationFromAttributes.cs @@ -70,10 +70,10 @@ private BuildConfigurationFromAttributes(AttributeData att) } public static VogenConfigurationBuildResult TryBuildFromValueObjectAttribute(AttributeData matchingAttribute) => - new BuildConfigurationFromAttributes(matchingAttribute).Build(false); + new BuildConfigurationFromAttributes(matchingAttribute).Build(argsAreFromVogenDefaultAttribute: false); public static VogenConfigurationBuildResult TryBuildFromVogenDefaultsAttribute(AttributeData matchingAttribute) => - new BuildConfigurationFromAttributes(matchingAttribute).Build(true); + new BuildConfigurationFromAttributes(matchingAttribute).Build(argsAreFromVogenDefaultAttribute: true); private VogenConfigurationBuildResult Build(bool argsAreFromVogenDefaultAttribute) { diff --git a/src/Vogen/Generators/ClassGenerator.cs b/src/Vogen/Generators/ClassGenerator.cs index 2087cae1ab..e7ecfa3b48 100644 --- a/src/Vogen/Generators/ClassGenerator.cs +++ b/src/Vogen/Generators/ClassGenerator.cs @@ -14,7 +14,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs b/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs deleted file mode 100644 index 0ac2791755..0000000000 --- a/src/Vogen/Generators/Conversions/GenerateOrleansConversions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Vogen.Generators.Conversions; - -public class GenerateOrleansConversions : IGenerateConversion, IGenerateAssemblyAttributes -{ - public string GenerateAnyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) => string.Empty; - - public string GenerateAnyBody(TypeDeclarationSyntax tds, VoWorkItem item) - { - if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) - { - return string.Empty; - } - - return $$""" - internal sealed class OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec<{{item.VoTypeName}}> - { - public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, {{item.VoTypeName}} value) - where TBufferWriter : global::System.Buffers.IBufferWriter - { - var baseCodec = writer.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); - baseCodec.WriteField(ref writer, fieldIdDelta, typeof({{item.UnderlyingTypeFullName}}), value.Value); - } - - public {{item.VoTypeName}} ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) - { - var baseCodec = reader.Session.CodecProvider.GetCodec<{{item.UnderlyingTypeFullName}}>(); - var baseValue = baseCodec.ReadValue(ref reader, field); - return {{item.VoTypeName}}.From(baseValue); - } - } - - internal sealed class OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier<{{item.VoTypeName}}> - { - public {{item.VoTypeName}} DeepCopy({{item.VoTypeName}} input, global::Orleans.Serialization.Cloning.CopyContext context) - { - return {{item.VoTypeName}}.From(input.Value); - } - } - - internal class OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase - { - protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) - { - config.Serializers.Add(typeof(OrleansSerializer)); - config.Copiers.Add(typeof(OrleansCopier)); - } - } - - """; - } - - public string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item) - { - if (!item.Config.Conversions.HasFlag(Vogen.Conversions.Orleans)) - { - return string.Empty; - } - - var voNamespace = item.FullNamespace; - - return string.IsNullOrWhiteSpace(voNamespace) - ? $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider({item.VoTypeName}.OrleansProvider))]" - : $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({voNamespace}.{item.VoTypeName}.OrleansProvider))]"; - } -} \ No newline at end of file diff --git a/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs b/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs deleted file mode 100644 index 2e71ddbc87..0000000000 --- a/src/Vogen/Generators/Conversions/IGenerateAssemblyAttributes.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Vogen.Generators.Conversions; - -public interface IGenerateAssemblyAttributes -{ - string GenerateAssemblyAttributes(TypeDeclarationSyntax tds, VoWorkItem item); -} \ No newline at end of file diff --git a/src/Vogen/Generators/RecordClassGenerator.cs b/src/Vogen/Generators/RecordClassGenerator.cs index c9afdcc787..863fff1b81 100644 --- a/src/Vogen/Generators/RecordClassGenerator.cs +++ b/src/Vogen/Generators/RecordClassGenerator.cs @@ -13,7 +13,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/RecordStructGenerator.cs b/src/Vogen/Generators/RecordStructGenerator.cs index c213efe529..5df709c51d 100644 --- a/src/Vogen/Generators/RecordStructGenerator.cs +++ b/src/Vogen/Generators/RecordStructGenerator.cs @@ -13,7 +13,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Generators/StructGenerator.cs b/src/Vogen/Generators/StructGenerator.cs index d42c275930..59ccc4242c 100644 --- a/src/Vogen/Generators/StructGenerator.cs +++ b/src/Vogen/Generators/StructGenerator.cs @@ -13,7 +13,6 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds) return $@" using Vogen; -{Util.GenerateAssemblyConversionAttributes(tds, item)} {Util.WriteStartNamespace(item.FullNamespace)} [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/src/Vogen/Util.cs b/src/Vogen/Util.cs index a25b2d9cb9..bc489beaff 100644 --- a/src/Vogen/Util.cs +++ b/src/Vogen/Util.cs @@ -22,8 +22,7 @@ public static class Util new GenerateTypeConverterConversions(), new GenerateDapperConversions(), new GenerateEfCoreTypeConversions(), - new GenerateLinqToDbConversions(), - new GenerateOrleansConversions(), + new GenerateLinqToDbConversions() }; public static string SanitizeToALegalFilename(string input) => input.Replace('@', '_'); @@ -215,23 +214,6 @@ public static string GenerateAnyConversionAttributes(TypeDeclarationSyntax tds, return sb.ToString(); } - public static string GenerateAssemblyConversionAttributes(TypeDeclarationSyntax tds, VoWorkItem item) - { - StringBuilder sb = new StringBuilder(); - - foreach (var conversionGenerator in _conversionGenerators.OfType()) - { - var attribute = conversionGenerator.GenerateAssemblyAttributes(tds, item); - - if (!string.IsNullOrEmpty(attribute)) - { - sb.Append(attribute); - } - } - - return sb.ToString(); - } - private static string GenerateAnyConversionAttributesForDebuggerProxy(VoWorkItem item) => item.Config.Conversions.ToString(); public static string GenerateAnyConversionBodies(TypeDeclarationSyntax tds, VoWorkItem item) diff --git a/src/Vogen/ValueObjectGenerator.cs b/src/Vogen/ValueObjectGenerator.cs index 5fd49a737b..69ae3c947b 100644 --- a/src/Vogen/ValueObjectGenerator.cs +++ b/src/Vogen/ValueObjectGenerator.cs @@ -99,6 +99,8 @@ private static void Execute( WriteEfCoreSpecs.WriteIfNeeded(spc, compilation, efCoreConverterSpecs); WriteBsonSerializers.WriteIfNeeded(spc, compilation, workItems); + + WriteOrleansSerializers.WriteIfNeeded(spc, workItems); if (workItems.Count > 0) { diff --git a/src/Vogen/WriteOrleansSerializers.cs b/src/Vogen/WriteOrleansSerializers.cs new file mode 100644 index 0000000000..6646fdb8df --- /dev/null +++ b/src/Vogen/WriteOrleansSerializers.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Vogen; + +internal class WriteOrleansSerializers +{ + public static void WriteIfNeeded(SourceProductionContext context, List workItems) + { + var items = workItems.Where(i => i.Config.Conversions.HasFlag(Conversions.Orleans)).ToList(); + + var toWrite = items.Select(GenerateSource).ToList(); + + foreach (var eachEntry in toWrite) + { + WriteSerializer(eachEntry, context); + } + } + + private static void WriteSerializer(SerializerEntry entry, SourceProductionContext context) + { + SourceText sourceText = SourceText.From(entry.SourceCode, Encoding.UTF8); + + Util.TryWriteUsingUniqueFilename(entry.Filename, context, sourceText); + } + + public record SerializerEntry(string Filename, string SourceCode); + + private static SerializerEntry GenerateSource(VoWorkItem item) + { + var fullNamespace = item.FullNamespace; + + var isPublic = item.WrapperType.DeclaredAccessibility.HasFlag(Accessibility.Public); + var accessor = isPublic ? "public" : "internal"; + + var ns = string.IsNullOrEmpty(fullNamespace) ? string.Empty : $"namespace {fullNamespace};"; + + string unescapedWrapperName = item.WrapperType.Name; + string wrapperName = Util.EscapeIfRequired(unescapedWrapperName); + string underlyingTypeName = item.UnderlyingTypeFullName; + + var assemblyAttribute = string.IsNullOrWhiteSpace(fullNamespace) + ? $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider({item.VoTypeName}OrleansProvider))]" + : $"[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof({fullNamespace}.{item.VoTypeName}OrleansProvider))]"; + + + string sb = + $$""" + {{GeneratedCodeSegments.Preamble}} + + {{assemblyAttribute}} + + {{ns}} + + {{accessor}} sealed class {{unescapedWrapperName}}OrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec<{{item.VoTypeName}}> + { + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, {{item.VoTypeName}} value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec<{{underlyingTypeName}}>(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof({{underlyingTypeName}}), value.Value); + } + + public {{item.VoTypeName}} ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec<{{underlyingTypeName}}>(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern {{wrapperName}} UnsafeDeserialize({{wrapperName}} @this, {{underlyingTypeName}} value); + + } + + {{accessor}} sealed class {{unescapedWrapperName}}OrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier<{{item.VoTypeName}}> + { + public {{item.VoTypeName}} DeepCopy({{item.VoTypeName}} input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return {{item.VoTypeName}}.From(input.Value); + } + } + + {{accessor}} class {{unescapedWrapperName}}OrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase + { + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof({{unescapedWrapperName}}OrleansSerializer)); + config.Copiers.Add(typeof({{unescapedWrapperName}}OrleansCopier)); + } + } + + """; + + var unsanitized = $"{item.WrapperType.ToDisplayString()}_orleans.g.cs"; + string filename = Util.SanitizeToALegalFilename(unsanitized); + return new SerializerEntry(filename, sb); + } +} \ No newline at end of file diff --git a/tests/AnalyzerTests/GlobalConfig/SadTests.cs b/tests/AnalyzerTests/GlobalConfig/SadTests.cs index 4a5351d6a3..466d51e5dc 100644 --- a/tests/AnalyzerTests/GlobalConfig/SadTests.cs +++ b/tests/AnalyzerTests/GlobalConfig/SadTests.cs @@ -178,7 +178,7 @@ public async Task Not_valid_conversion() var source = @"using System; using Vogen; -[assembly: VogenDefaults(conversions: (Conversions)666)] +[assembly: VogenDefaults(conversions: (Conversions)4242)] namespace Whatever; @@ -279,7 +279,7 @@ public async Task Not_valid_customization_or_conversion() var source = @"using System; using Vogen; -[assembly: VogenDefaults(customizations: (Customizations)666, conversions: (Conversions)666)] +[assembly: VogenDefaults(customizations: (Customizations)666, conversions: (Conversions)4242)] namespace Whatever; diff --git a/tests/AnalyzerTests/LocalConfig/SadTests.cs b/tests/AnalyzerTests/LocalConfig/SadTests.cs index 44c85548d8..ba9bb5a660 100644 --- a/tests/AnalyzerTests/LocalConfig/SadTests.cs +++ b/tests/AnalyzerTests/LocalConfig/SadTests.cs @@ -193,7 +193,7 @@ public async Task Not_valid_conversion(string type) namespace Whatever; -[ValueObject(conversions: (Conversions)666)] +[ValueObject(conversions: (Conversions)4242)] public {type} CustomerId {{ }} "; diff --git a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs index a3039c7d66..00d6ba3e05 100644 --- a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs +++ b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; using Shared; using Vogen; @@ -7,7 +8,7 @@ namespace SnapshotTests.OrleansGeneration; public class OrleansGenerationTests { [Fact] - public async Task Do() + public async Task Generates_if_global_attribute_set_and_on_net_8_or_greater() { var source = """ using System; @@ -30,4 +31,102 @@ public partial struct Name; .IgnoreInitialCompilationErrors() .RunOn(TargetFramework.Net8_0); } + + [Fact] + public async Task Generates_if_local_attribute_set_and_on_net_8_or_greater() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace OrleansTests; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Age; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Name; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } + + [Fact] + public async Task Takes_on_accessibility_of_value_object() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace OrleansTests; + + [ValueObject(conversions: Conversions.Orleans)] + internal partial struct Age; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Name; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } + + [Fact] + public async Task Escapes_wrapper_name() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace @class.@struct.@record; + + [ValueObject(conversions: Conversions.Orleans)] + internal partial struct @class; + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .RunOn(TargetFramework.Net8_0); + } + + [Fact] + public async Task Skipped_on_csharp_less_than_12() + { + var source = """ + using System; + using Vogen; + + [assembly: VogenDefaults(conversions: Conversions.None)] + + namespace OrleansTests; + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Age { } + + [ValueObject(conversions: Conversions.Orleans)] + public partial struct Name { } + """; + + await new SnapshotRunner() + .WithSource(source) + .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) + .IgnoreInitialCompilationErrors() + .WithLanguageVersion(LanguageVersion.CSharp11) + .RunOn(TargetFramework.Net7_0); + } } \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_charp_less_than_12.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v7.0/OrleansGenerationTests.Skipped_on_net_7.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set_and_on_net_8_or_greater.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file From 69125050c193a07c8f584d7f14e8158e68f30e8d Mon Sep 17 00:00:00 2001 From: Sander van 't Einde Date: Tue, 6 Aug 2024 08:10:54 +0200 Subject: [PATCH 7/9] Orleans example --- Consumers.sln | 6 +++ samples/OrleansExample/CustomUrl.cs | 15 ++++++ samples/OrleansExample/IUrlShortenerGrain.cs | 7 +++ samples/OrleansExample/OrleansExample.csproj | 22 ++++++++ samples/OrleansExample/OrleansExample.http | 9 ++++ samples/OrleansExample/Program.cs | 51 +++++++++++++++++++ .../Properties/launchSettings.json | 31 +++++++++++ samples/OrleansExample/README.md | 7 +++ samples/OrleansExample/UrlDetails.cs | 11 ++++ samples/OrleansExample/UrlShortenerGrain.cs | 23 +++++++++ .../appsettings.Development.json | 8 +++ samples/OrleansExample/appsettings.json | 9 ++++ 12 files changed, 199 insertions(+) create mode 100644 samples/OrleansExample/CustomUrl.cs create mode 100644 samples/OrleansExample/IUrlShortenerGrain.cs create mode 100644 samples/OrleansExample/OrleansExample.csproj create mode 100644 samples/OrleansExample/OrleansExample.http create mode 100644 samples/OrleansExample/Program.cs create mode 100644 samples/OrleansExample/Properties/launchSettings.json create mode 100644 samples/OrleansExample/README.md create mode 100644 samples/OrleansExample/UrlDetails.cs create mode 100644 samples/OrleansExample/UrlShortenerGrain.cs create mode 100644 samples/OrleansExample/appsettings.Development.json create mode 100644 samples/OrleansExample/appsettings.json diff --git a/Consumers.sln b/Consumers.sln index ffd9da63f9..86528adb08 100644 --- a/Consumers.sln +++ b/Consumers.sln @@ -32,6 +32,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Onion", "Onion", "{48796EAD EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infra", "samples\Onion\Infra\Infra.csproj", "{03EA902C-68D5-44B2-A8F0-2F3325B05448}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrleansExample", "samples\OrleansExample\OrleansExample.csproj", "{2F496E59-0462-431A-A70B-266B58A9A672}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,6 +68,10 @@ Global {03EA902C-68D5-44B2-A8F0-2F3325B05448}.Debug|Any CPU.Build.0 = Debug|Any CPU {03EA902C-68D5-44B2-A8F0-2F3325B05448}.Release|Any CPU.ActiveCfg = Release|Any CPU {03EA902C-68D5-44B2-A8F0-2F3325B05448}.Release|Any CPU.Build.0 = Release|Any CPU + {2F496E59-0462-431A-A70B-266B58A9A672}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F496E59-0462-431A-A70B-266B58A9A672}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F496E59-0462-431A-A70B-266B58A9A672}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F496E59-0462-431A-A70B-266B58A9A672}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/OrleansExample/CustomUrl.cs b/samples/OrleansExample/CustomUrl.cs new file mode 100644 index 0000000000..0427157fd0 --- /dev/null +++ b/samples/OrleansExample/CustomUrl.cs @@ -0,0 +1,15 @@ +using Vogen; + +namespace OrleansExample; + +[ValueObject(Conversions.Default | Conversions.Orleans)] +public partial class CustomUrl +{ + private static Validation Validate(string value) + { + // Just for example’s sake, a real Url validator should be more complex then this. + return value.StartsWith("http") + ? Validation.Ok + : Validation.Invalid("A url should start with http"); + } +} \ No newline at end of file diff --git a/samples/OrleansExample/IUrlShortenerGrain.cs b/samples/OrleansExample/IUrlShortenerGrain.cs new file mode 100644 index 0000000000..7dacca92c9 --- /dev/null +++ b/samples/OrleansExample/IUrlShortenerGrain.cs @@ -0,0 +1,7 @@ +namespace OrleansExample; + +public interface IUrlShortenerGrain : IGrainWithStringKey +{ + Task SetUrl(CustomUrl fullUrl); + Task GetUrl(); +} \ No newline at end of file diff --git a/samples/OrleansExample/OrleansExample.csproj b/samples/OrleansExample/OrleansExample.csproj new file mode 100644 index 0000000000..91664da7d6 --- /dev/null +++ b/samples/OrleansExample/OrleansExample.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + true + $(NoWarn),NU1603,NU1903 + + + + + + + + + + + + + + diff --git a/samples/OrleansExample/OrleansExample.http b/samples/OrleansExample/OrleansExample.http new file mode 100644 index 0000000000..1e55215604 --- /dev/null +++ b/samples/OrleansExample/OrleansExample.http @@ -0,0 +1,9 @@ +@OrleansExample_HostAddress = http://localhost:5242 + +### Create shortened URL +GET {{OrleansExample_HostAddress}}/shorten?url=https%3A%2F%2Fgithub.com%2FSteveDunn%2FVogen +Accept: application/json + +### Create shortened URL that does not meet validation, this generated a 400 Bad Request +GET {{OrleansExample_HostAddress}}/shorten?url=invalidUrl +Accept: application/json diff --git a/samples/OrleansExample/Program.cs b/samples/OrleansExample/Program.cs new file mode 100644 index 0000000000..259e6456aa --- /dev/null +++ b/samples/OrleansExample/Program.cs @@ -0,0 +1,51 @@ +using Orleans.Runtime; +using OrleansExample; + +var builder = WebApplication.CreateBuilder(args); + +builder.Host.UseOrleans(static siloBuilder => +{ + siloBuilder.UseLocalhostClustering(); + siloBuilder.AddMemoryGrainStorage("urls"); +}); + +var app = builder.Build(); + +app.MapGet("/", static () => "Welcome to the URL shortener, powered by Orleans!"); + +app.MapGet("/shorten", + static async (IGrainFactory grains, HttpRequest request, CustomUrl url) => + { + var host = $"{request.Scheme}://{request.Host.Value}"; + + // Create a unique, short ID + var shortenedRouteSegment = Guid.NewGuid().GetHashCode().ToString("X"); + + // Create and persist a grain with the shortened ID and full URL + var shortenerGrain = + grains.GetGrain(shortenedRouteSegment); + + await shortenerGrain.SetUrl(url); + + // Return the shortened URL for later use + var resultBuilder = new UriBuilder(host) + { + Path = $"/go/{shortenedRouteSegment}" + }; + + return Results.Ok(resultBuilder.Uri); + }); + +app.MapGet("/go/{shortenedRouteSegment:required}", + static async (IGrainFactory grains, string shortenedRouteSegment) => + { + // Retrieve the grain using the shortened ID and url to the original URL + var shortenerGrain = + grains.GetGrain(shortenedRouteSegment); + + var url = await shortenerGrain.GetUrl(); + + return Results.Redirect(url.Value); + }); + +app.Run(); \ No newline at end of file diff --git a/samples/OrleansExample/Properties/launchSettings.json b/samples/OrleansExample/Properties/launchSettings.json new file mode 100644 index 0000000000..421a263d10 --- /dev/null +++ b/samples/OrleansExample/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:59890", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "http://localhost:5242", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/OrleansExample/README.md b/samples/OrleansExample/README.md new file mode 100644 index 0000000000..96fe28ceaa --- /dev/null +++ b/samples/OrleansExample/README.md @@ -0,0 +1,7 @@ +# Orleans example + +This project showcases how Vogen can be utilized within the [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/) framework. + +It showcases the [quickstart example](https://learn.microsoft.com/en-us/dotnet/orleans/quickstarts/build-your-first-orleans-app?tabs=visual-studio) but with Vogen value objects. + +The [http file](./OrleansExample.http) can be utilized to test the API running Orleans. \ No newline at end of file diff --git a/samples/OrleansExample/UrlDetails.cs b/samples/OrleansExample/UrlDetails.cs new file mode 100644 index 0000000000..ac33838c33 --- /dev/null +++ b/samples/OrleansExample/UrlDetails.cs @@ -0,0 +1,11 @@ +namespace OrleansExample; + +[GenerateSerializer, Alias(nameof(UrlDetails))] +public sealed record UrlDetails +{ + [Id(0)] + public required CustomUrl FullUrl { get; init; } + + [Id(1)] + public required string ShortenedRouteSegment { get; init; } +} \ No newline at end of file diff --git a/samples/OrleansExample/UrlShortenerGrain.cs b/samples/OrleansExample/UrlShortenerGrain.cs new file mode 100644 index 0000000000..d94276fe59 --- /dev/null +++ b/samples/OrleansExample/UrlShortenerGrain.cs @@ -0,0 +1,23 @@ +namespace OrleansExample; + +public class UrlShortenerGrain( + [PersistentState(stateName: "url", storageName: "urls")] + IPersistentState state +) : Grain, IUrlShortenerGrain +{ + public async Task SetUrl(CustomUrl fullUrl) + { + state.State = new() + { + ShortenedRouteSegment = this.GetPrimaryKeyString(), + FullUrl = fullUrl + }; + + await state.WriteStateAsync(); + } + + public Task GetUrl() + { + return Task.FromResult(state.State.FullUrl); + } +} \ No newline at end of file diff --git a/samples/OrleansExample/appsettings.Development.json b/samples/OrleansExample/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/samples/OrleansExample/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/OrleansExample/appsettings.json b/samples/OrleansExample/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/samples/OrleansExample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} From 67bc02c4785afe551194f256afe048dd9ef40c58 Mon Sep 17 00:00:00 2001 From: Steve Dunn Date: Tue, 6 Aug 2024 07:13:32 +0100 Subject: [PATCH 8/9] Rename and update tests --- .../OrleansGenerationTests.cs | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs index 00d6ba3e05..a2c685bb0d 100644 --- a/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs +++ b/tests/SnapshotTests/OrleansGeneration/OrleansGenerationTests.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; using Shared; using Vogen; @@ -8,7 +7,7 @@ namespace SnapshotTests.OrleansGeneration; public class OrleansGenerationTests { [Fact] - public async Task Generates_if_global_attribute_set_and_on_net_8_or_greater() + public async Task Generates_if_global_attribute_set() { var source = """ using System; @@ -33,7 +32,7 @@ public partial struct Name; } [Fact] - public async Task Generates_if_local_attribute_set_and_on_net_8_or_greater() + public async Task Generates_if_local_attribute_set() { var source = """ using System; @@ -103,30 +102,4 @@ internal partial struct @class; .IgnoreInitialCompilationErrors() .RunOn(TargetFramework.Net8_0); } - - [Fact] - public async Task Skipped_on_csharp_less_than_12() - { - var source = """ - using System; - using Vogen; - - [assembly: VogenDefaults(conversions: Conversions.None)] - - namespace OrleansTests; - - [ValueObject(conversions: Conversions.Orleans)] - public partial struct Age { } - - [ValueObject(conversions: Conversions.Orleans)] - public partial struct Name { } - """; - - await new SnapshotRunner() - .WithSource(source) - .WithPackage(new NuGetPackage("Microsoft.Orleans.Serialization", "8.2.0", string.Empty)) - .IgnoreInitialCompilationErrors() - .WithLanguageVersion(LanguageVersion.CSharp11) - .RunOn(TargetFramework.Net7_0); - } } \ No newline at end of file From 8bfadaf43045a3d25fb1a77d43f685bb66809259 Mon Sep 17 00:00:00 2001 From: Steve Dunn Date: Tue, 6 Aug 2024 07:28:07 +0100 Subject: [PATCH 9/9] Snapshot tests --- ...ionTests.Escapes_wrapper_name.verified.txt | 667 ++++++++++- ...rates_if_global_attribute_set.verified.txt | 1024 ++++++++++++++++ ...erates_if_local_attribute_set.verified.txt | 1024 ++++++++++++++++ ...accessibility_of_value_object.verified.txt | 1025 ++++++++++++++++- 4 files changed, 3738 insertions(+), 2 deletions(-) create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_global_attribute_set.verified.txt create mode 100644 tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set.verified.txt diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt index 5f282702bb..794e287444 100644 --- a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Escapes_wrapper_name.verified.txt @@ -1 +1,666 @@ - \ No newline at end of file +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(@class.@struct.@record.@classOrleansProvider))] + +namespace @class.@struct.@record; + +internal sealed class classOrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec<@class> +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, @class value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.Int32), value.Value); + } + + public @class ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern @class UnsafeDeserialize(@class @this, System.Int32 value); + +} + +internal sealed class classOrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier<@class> +{ + public @class DeepCopy(@class input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return @class.From(input.Value); + } +} + +internal class classOrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(classOrleansSerializer)); + config.Copiers.Add(typeof(classOrleansCopier)); + } +} + + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +namespace generator; + +public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory +{ + public VogenTypesFactory() { } + private static readonly global::System.Collections.Generic.Dictionary> _lookup = + new global::System.Collections.Generic.Dictionary> { + + }; + + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => + _lookup[typeToConvert].Value; +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; + +namespace @class.@struct.@record +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(@classDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.Int32, Value = { _value }")] + internal partial struct @class : global::System.IEquatable<@class>, global::System.IEquatable , global::System.IComparable<@class>, global::System.IComparable, global::System.IParsable<@class>, global::System.ISpanParsable<@class>, global::System.IUtf8SpanParsable<@class> + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.Int32 _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.Int32 Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public @class() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private @class(System.Int32 value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static @class From(System.Int32 value) + { + + + + + @class instance = new @class(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.Int32 value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class vo) +{ + + + + + + vo = new @class(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError<@class> TryFrom(System.Int32 value) +{ + + + + + + + return new ValueObjectOrError<@class>(new @class(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator @class(System.Int32 value) => From(value); + public static explicit operator System.Int32(@class value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static @class __Deserialize(System.Int32 value) + { + + + + + return new @class(value); + } + public readonly global::System.Boolean Equals(@class other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(@class other, global::System.Collections.Generic.IEqualityComparer<@class> comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.Int32 primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is @class && Equals((@class) obj); + } + + public static global::System.Boolean operator ==(@class left, @class right) => Equals(left, right); + public static global::System.Boolean operator !=(@class left, @class right) => !(left == right); + + public static global::System.Boolean operator ==(@class left, System.Int32 right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(@class left, System.Int32 right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.Int32 left, @class right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.Int32 left, @class right) => !Equals(left, right.Value); + + public int CompareTo(@class other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is @class x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type @class", nameof(other)); + } + + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(utf8Text, style, provider, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(utf8Text, provider, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(utf8Text, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out @class result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new @class(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(string s) { + var r = System.Int32.Parse(s); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(string s, global::System.Globalization.NumberStyles style) { + var r = System.Int32.Parse(s, style); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static @class Parse(string s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + + + + + internal sealed class @classDebugView + { + private readonly @class _t; + + @classDebugView(@class t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Int32"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_global_attribute_set.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_global_attribute_set.verified.txt new file mode 100644 index 0000000000..354dcb0f56 --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_global_attribute_set.verified.txt @@ -0,0 +1,1024 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.AgeOrleansProvider))] + +namespace OrleansTests; + +public sealed class AgeOrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Age value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.Int32), value.Value); + } + + public Age ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern Age UnsafeDeserialize(Age @this, System.Int32 value); + +} + +public sealed class AgeOrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Age DeepCopy(Age input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Age.From(input.Value); + } +} + +public class AgeOrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(AgeOrleansSerializer)); + config.Copiers.Add(typeof(AgeOrleansCopier)); + } +} + + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.NameOrleansProvider))] + +namespace OrleansTests; + +public sealed class NameOrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Name value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.String), value.Value); + } + + public Name ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern Name UnsafeDeserialize(Name @this, System.String value); + +} + +public sealed class NameOrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Name DeepCopy(Name input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Name.From(input.Value); + } +} + +public class NameOrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(NameOrleansSerializer)); + config.Copiers.Add(typeof(NameOrleansCopier)); + } +} + + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +namespace generator; + +public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory +{ + public VogenTypesFactory() { } + private static readonly global::System.Collections.Generic.Dictionary> _lookup = + new global::System.Collections.Generic.Dictionary> { + + }; + + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => + _lookup[typeToConvert].Value; +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(AgeDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.Int32, Value = { _value }")] + public partial struct Age : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.ISpanParsable, global::System.IUtf8SpanParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.Int32 _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.Int32 Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Age() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Age(System.Int32 value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Age From(System.Int32 value) + { + + + + + Age instance = new Age(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.Int32 value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age vo) +{ + + + + + + vo = new Age(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.Int32 value) +{ + + + + + + + return new ValueObjectOrError(new Age(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Age(System.Int32 value) => From(value); + public static explicit operator System.Int32(Age value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Age __Deserialize(System.Int32 value) + { + + + + + return new Age(value); + } + public readonly global::System.Boolean Equals(Age other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Age other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.Int32 primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Age && Equals((Age) obj); + } + + public static global::System.Boolean operator ==(Age left, Age right) => Equals(left, right); + public static global::System.Boolean operator !=(Age left, Age right) => !(left == right); + + public static global::System.Boolean operator ==(Age left, System.Int32 right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Age left, System.Int32 right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.Int32 left, Age right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.Int32 left, Age right) => !Equals(left, right.Value); + + public int CompareTo(Age other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Age x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Age", nameof(other)); + } + + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s) { + var r = System.Int32.Parse(s); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style) { + var r = System.Int32.Parse(s, style); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + + + + + internal sealed class AgeDebugView + { + private readonly Age _t; + + AgeDebugView(Age t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Int32"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(NameDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.String, Value = { _value }")] + public partial struct Name : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.String _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.String Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Name() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Name(System.String value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Name From(System.String value) + { + + + + + Name instance = new Name(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.String value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name vo) +{ + + + + + + vo = new Name(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.String value) +{ + + + + + + + return new ValueObjectOrError(new Name(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Name(System.String value) => From(value); + public static explicit operator System.String(Name value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Name __Deserialize(System.String value) + { + + + + + return new Name(value); + } + public readonly global::System.Boolean Equals(Name other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Name other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.String primitive) + { + return Value.Equals(primitive); + } + + public readonly global::System.Boolean Equals(System.String primitive, global::System.StringComparer comparer) + { + return comparer.Equals(Value, primitive); + } + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Name && Equals((Name) obj); + } + + public static global::System.Boolean operator ==(Name left, Name right) => Equals(left, right); + public static global::System.Boolean operator !=(Name left, Name right) => !(left == right); + + public static global::System.Boolean operator ==(Name left, System.String right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Name left, System.String right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.String left, Name right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.String left, Name right) => !Equals(left, right.Value); + + public int CompareTo(Name other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Name x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Name", nameof(other)); + } + + + /// + /// + /// + /// True if the value passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.String s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name result) { + + + result = new Name(s); + return true; + } + /// + /// + /// + /// The value created via the method. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Name Parse(global::System.String s, global::System.IFormatProvider provider) { + return From(s); + } + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + + + + + internal sealed class NameDebugView + { + private readonly Name _t; + + NameDebugView(Name t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.String"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set.verified.txt new file mode 100644 index 0000000000..354dcb0f56 --- /dev/null +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Generates_if_local_attribute_set.verified.txt @@ -0,0 +1,1024 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.AgeOrleansProvider))] + +namespace OrleansTests; + +public sealed class AgeOrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Age value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.Int32), value.Value); + } + + public Age ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern Age UnsafeDeserialize(Age @this, System.Int32 value); + +} + +public sealed class AgeOrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Age DeepCopy(Age input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Age.From(input.Value); + } +} + +public class AgeOrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(AgeOrleansSerializer)); + config.Copiers.Add(typeof(AgeOrleansCopier)); + } +} + + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.NameOrleansProvider))] + +namespace OrleansTests; + +public sealed class NameOrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Name value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.String), value.Value); + } + + public Name ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern Name UnsafeDeserialize(Name @this, System.String value); + +} + +public sealed class NameOrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Name DeepCopy(Name input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Name.From(input.Value); + } +} + +public class NameOrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(NameOrleansSerializer)); + config.Copiers.Add(typeof(NameOrleansCopier)); + } +} + + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +namespace generator; + +public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory +{ + public VogenTypesFactory() { } + private static readonly global::System.Collections.Generic.Dictionary> _lookup = + new global::System.Collections.Generic.Dictionary> { + + }; + + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => + _lookup[typeToConvert].Value; +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(AgeDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.Int32, Value = { _value }")] + public partial struct Age : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.ISpanParsable, global::System.IUtf8SpanParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.Int32 _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.Int32 Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Age() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Age(System.Int32 value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Age From(System.Int32 value) + { + + + + + Age instance = new Age(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.Int32 value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age vo) +{ + + + + + + vo = new Age(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.Int32 value) +{ + + + + + + + return new ValueObjectOrError(new Age(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Age(System.Int32 value) => From(value); + public static explicit operator System.Int32(Age value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Age __Deserialize(System.Int32 value) + { + + + + + return new Age(value); + } + public readonly global::System.Boolean Equals(Age other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Age other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.Int32 primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Age && Equals((Age) obj); + } + + public static global::System.Boolean operator ==(Age left, Age right) => Equals(left, right); + public static global::System.Boolean operator !=(Age left, Age right) => !(left == right); + + public static global::System.Boolean operator ==(Age left, System.Int32 right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Age left, System.Int32 right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.Int32 left, Age right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.Int32 left, Age right) => !Equals(left, right.Value); + + public int CompareTo(Age other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Age x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Age", nameof(other)); + } + + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s) { + var r = System.Int32.Parse(s); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style) { + var r = System.Int32.Parse(s, style); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + + + + + internal sealed class AgeDebugView + { + private readonly Age _t; + + AgeDebugView(Age t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Int32"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(NameDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.String, Value = { _value }")] + public partial struct Name : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.String _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.String Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Name() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Name(System.String value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Name From(System.String value) + { + + + + + Name instance = new Name(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.String value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name vo) +{ + + + + + + vo = new Name(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.String value) +{ + + + + + + + return new ValueObjectOrError(new Name(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Name(System.String value) => From(value); + public static explicit operator System.String(Name value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Name __Deserialize(System.String value) + { + + + + + return new Name(value); + } + public readonly global::System.Boolean Equals(Name other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Name other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.String primitive) + { + return Value.Equals(primitive); + } + + public readonly global::System.Boolean Equals(System.String primitive, global::System.StringComparer comparer) + { + return comparer.Equals(Value, primitive); + } + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Name && Equals((Name) obj); + } + + public static global::System.Boolean operator ==(Name left, Name right) => Equals(left, right); + public static global::System.Boolean operator !=(Name left, Name right) => !(left == right); + + public static global::System.Boolean operator ==(Name left, System.String right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Name left, System.String right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.String left, Name right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.String left, Name right) => !Equals(left, right.Value); + + public int CompareTo(Name other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Name x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Name", nameof(other)); + } + + + /// + /// + /// + /// True if the value passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.String s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name result) { + + + result = new Name(s); + return true; + } + /// + /// + /// + /// The value created via the method. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Name Parse(global::System.String s, global::System.IFormatProvider provider) { + return From(s); + } + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + + + + + internal sealed class NameDebugView + { + private readonly Name _t; + + NameDebugView(Name t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.String"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt index 5f282702bb..7970b1a5e2 100644 --- a/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt +++ b/tests/SnapshotTests/OrleansGeneration/snapshots/snap-v8.0/OrleansGenerationTests.Takes_on_accessibility_of_value_object.verified.txt @@ -1 +1,1024 @@ - \ No newline at end of file +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.AgeOrleansProvider))] + +namespace OrleansTests; + +internal sealed class AgeOrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Age value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.Int32), value.Value); + } + + public Age ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern Age UnsafeDeserialize(Age @this, System.Int32 value); + +} + +internal sealed class AgeOrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Age DeepCopy(Age input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Age.From(input.Value); + } +} + +internal class AgeOrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(AgeOrleansSerializer)); + config.Copiers.Add(typeof(AgeOrleansCopier)); + } +} + + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +[assembly: global::Orleans.Serialization.Configuration.TypeManifestProvider(typeof(OrleansTests.NameOrleansProvider))] + +namespace OrleansTests; + +public sealed class NameOrleansSerializer : global::Orleans.Serialization.Codecs.IFieldCodec +{ + public void WriteField(ref global::Orleans.Serialization.Buffers.Writer writer, uint fieldIdDelta, System.Type expectedType, Name value) + where TBufferWriter : global::System.Buffers.IBufferWriter + { + var baseCodec = writer.Session.CodecProvider.GetCodec(); + baseCodec.WriteField(ref writer, fieldIdDelta, typeof(System.String), value.Value); + } + + public Name ReadValue(ref global::Orleans.Serialization.Buffers.Reader reader, global::Orleans.Serialization.WireProtocol.Field field) + { + var baseCodec = reader.Session.CodecProvider.GetCodec(); + var baseValue = baseCodec.ReadValue(ref reader, field); + return UnsafeDeserialize(default, baseValue); + } + + [global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.StaticMethod, Name = "__Deserialize")] + static extern Name UnsafeDeserialize(Name @this, System.String value); + +} + +public sealed class NameOrleansCopier : global::Orleans.Serialization.Cloning.IDeepCopier +{ + public Name DeepCopy(Name input, global::Orleans.Serialization.Cloning.CopyContext context) + { + return Name.From(input.Value); + } +} + +public class NameOrleansProvider : global::Orleans.Serialization.Configuration.TypeManifestProviderBase +{ + protected override void ConfigureInner(global::Orleans.Serialization.Configuration.TypeManifestOptions config) + { + config.Serializers.Add(typeof(NameOrleansSerializer)); + config.Copiers.Add(typeof(NameOrleansCopier)); + } +} + + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +namespace generator; + +public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory +{ + public VogenTypesFactory() { } + private static readonly global::System.Collections.Generic.Dictionary> _lookup = + new global::System.Collections.Generic.Dictionary> { + + }; + + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => + _lookup[typeToConvert].Value; +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(AgeDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.Int32, Value = { _value }")] + internal partial struct Age : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.ISpanParsable, global::System.IUtf8SpanParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.Int32 _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.Int32 Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Age() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Age(System.Int32 value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Age From(System.Int32 value) + { + + + + + Age instance = new Age(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.Int32 value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age vo) +{ + + + + + + vo = new Age(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.Int32 value) +{ + + + + + + + return new ValueObjectOrError(new Age(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Age(System.Int32 value) => From(value); + public static explicit operator System.Int32(Age value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Age __Deserialize(System.Int32 value) + { + + + + + return new Age(value); + } + public readonly global::System.Boolean Equals(Age other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Age other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.Int32 primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Age && Equals((Age) obj); + } + + public static global::System.Boolean operator ==(Age left, Age right) => Equals(left, right); + public static global::System.Boolean operator !=(Age left, Age right) => !(left == right); + + public static global::System.Boolean operator ==(Age left, System.Int32 right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Age left, System.Int32 right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.Int32 left, Age right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.Int32 left, Age right) => !Equals(left, right.Value); + + public int CompareTo(Age other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Age x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Age", nameof(other)); + } + + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan utf8Text, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(utf8Text, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.ReadOnlySpan s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, style, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, provider, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// True if the value could a) be parsed by the underlying type, and b) passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(string s, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Age result) { + if(System.Int32.TryParse(s, out var __v)) { + + + result = new Age(__v); + return true; + } + + result = default; + return false; + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan utf8Text, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(utf8Text, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(global::System.ReadOnlySpan s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s) { + var r = System.Int32.Parse(s); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style) { + var r = System.Int32.Parse(s, style); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, style, provider); + return From(r); + } + + /// + /// + /// + /// + /// The value created by calling the Parse method on the primitive. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Age Parse(string s, global::System.IFormatProvider provider) { + var r = System.Int32.Parse(s, provider); + return From(r); + } + + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + + + + + internal sealed class AgeDebugView + { + private readonly Age _t; + + AgeDebugView(Age t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Int32"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 + +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 + +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 + +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669 + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 + +using Vogen; + +namespace OrleansTests +{ + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(NameDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.String, Value = { _value }")] + public partial struct Name : global::System.IEquatable, global::System.IEquatable , global::System.IComparable, global::System.IComparable, global::System.IParsable + { +#if DEBUG + private readonly global::System.Diagnostics.StackTrace _stackTrace = null; +#endif + +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + + private readonly System.String _value; + + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly System.String Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + get + { + EnsureInitialized(); + return _value; + } + } + + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Name() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif + +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Name(System.String value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + public static Name From(System.String value) + { + + + + + Name instance = new Name(value); + + return instance; + } + + /// +/// Tries to build an instance from the provided underlying type. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, false will be returned. +/// +/// The underlying type. +/// An instance of the value object. +/// True if the value object can be built, otherwise false. +public static bool TryFrom(System.String value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name vo) +{ + + + + + + vo = new Name(value); + + return true; +}/// +/// Tries to build an instance from the provided underlying value. +/// If a normalization method is provided, it will be called. +/// If validation is provided, and it fails, an error will be returned. +/// +/// The primitive value. +/// A containing either the value object, or an error. +public static ValueObjectOrError TryFrom(System.String value) +{ + + + + + + + return new ValueObjectOrError(new Name(value)); +} + +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION + public readonly bool IsInitialized() => true; +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + + + + public static explicit operator Name(System.String value) => From(value); + public static explicit operator System.String(Name value) => value.Value; + + // only called internally when something has been deserialized into + // its primitive type. + private static Name __Deserialize(System.String value) + { + + + + + return new Name(value); + } + public readonly global::System.Boolean Equals(Name other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if(!IsInitialized() || !other.IsInitialized()) return false; + + return global::System.Collections.Generic.EqualityComparer.Default.Equals(Value, other.Value); + } + public global::System.Boolean Equals(Name other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + + public readonly global::System.Boolean Equals(System.String primitive) + { + return Value.Equals(primitive); + } + + public readonly global::System.Boolean Equals(System.String primitive, global::System.StringComparer comparer) + { + return comparer.Equals(Value, primitive); + } + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Name && Equals((Name) obj); + } + + public static global::System.Boolean operator ==(Name left, Name right) => Equals(left, right); + public static global::System.Boolean operator !=(Name left, Name right) => !(left == right); + + public static global::System.Boolean operator ==(Name left, System.String right) => Equals(left.Value, right); + public static global::System.Boolean operator !=(Name left, System.String right) => !Equals(left.Value, right); + + public static global::System.Boolean operator ==(System.String left, Name right) => Equals(left, right.Value); + public static global::System.Boolean operator !=(System.String left, Name right) => !Equals(left, right.Value); + + public int CompareTo(Name other) => Value.CompareTo(other.Value); + public int CompareTo(object other) { + if(other is null) return 1; + if(other is Name x) return CompareTo(x); + throw new global::System.ArgumentException("Cannot compare to object as it is not of type Name", nameof(other)); + } + + + /// + /// + /// + /// True if the value passes any validation (after running any optional normalization). + /// + public static global::System.Boolean TryParse(global::System.String s, global::System.IFormatProvider provider, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + out Name result) { + + + result = new Name(s); + return true; + } + /// + /// + /// + /// The value created via the method. + /// + /// Thrown when the value can be parsed, but is not valid. + public static Name Parse(global::System.String s, global::System.IFormatProvider provider) { + return From(s); + } + + + public readonly override global::System.Int32 GetHashCode() + { + return global::System.Collections.Generic.EqualityComparer.Default.GetHashCode(Value); + } + + /// Returns the string representation of the underlying . + public readonly override global::System.String ToString() =>IsInitialized() ? Value.ToString() : "[UNINITIALIZED]"; + + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? ""; +#else + global::System.String message = "Use of uninitialized Value Object."; +#endif + + throw new global::Vogen.ValueObjectValidationException(message); + } + } + + + + + + + + + + + + + internal sealed class NameDebugView + { + private readonly Name _t; + + NameDebugView(Name t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.String"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]" ; + + #if DEBUG + public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method"; + #endif + + public global::System.String Conversions => @"Orleans"; + } + +} + +} +] \ No newline at end of file