/// <summary> /// Constructs a new <see cref="CSharpProperty" /> object. /// </summary> /// <param name="name">The name of this property.</param> /// <param name="type">The type of this property.</param> /// <param name="accessModifier">The access level of this property.</param> /// <param name="isStatic">Whether or not this is a static property.</param> /// <param name="isOverride">Whether or not this is overriding a property in a base type.</param> /// <param name="hasGetter">Whether or not this property has a getter.</param> /// <param name="hasSetter">Whether or not this property has a setter.</param> /// <param name="defaultValue">The default value for this property.</param> /// <param name="attributes">The attributes on this property.</param> /// <param name="documentationComment">The documentation comment for this property.</param> public CSharpProperty( string name, string type, CSharpAccessModifier accessModifier = CSharpAccessModifier.Public, bool isStatic = false, bool isOverride = false, bool hasGetter = true, bool hasSetter = true, string defaultValue = null, IEnumerable <CSharpAttribute> attributes = null, CSharpDocumentationComment documentationComment = null) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("C# Property name cannot be null or whitespace", nameof(name)); } if (type != null && string.IsNullOrWhiteSpace(type)) { throw new ArgumentException("Property type cannot be empty or whitespace", nameof(name)); } this.Name = name; this.TypeName = type ?? throw new ArgumentNullException(nameof(type)); this.IsStatic = isStatic; this.IsOverride = isOverride; this.AccessModifier = accessModifier; this.HasGetter = hasGetter; this.HasSetter = hasSetter; this.DefaultValue = defaultValue; this.Attributes = attributes ?? Array.Empty <CSharpAttribute>(); this.DocumentationComment = documentationComment; }
/// <summary> /// Asserts that the selected property has a getter with the specified C# access modifier. /// </summary> /// <param name="accessModifier">The expected C# access modifier.</param> /// <param name="because"> /// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion /// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically. /// </param> /// <param name="becauseArgs"> /// Zero or more objects to format using the placeholders in <see cref="because" />. /// </param> public AndConstraint <PropertyInfoAssertions> BeReadable(CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { Subject.Should().BeReadable(because, becauseArgs); Subject.GetGetMethod(true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); return(new AndConstraint <PropertyInfoAssertions>(this)); }
/// <summary> /// Asserts that the selected member does not have the specified C# <paramref name="accessModifier"/>. /// </summary> /// <param name="accessModifier">The unexpected C# access modifier.</param> /// <param name="because"> /// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion /// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically. /// </param> /// <param name="becauseArgs"> /// Zero or more objects to format using the placeholders in <see cref="because" />. /// </param> public AndConstraint <TAssertions> NotHaveAccessModifier(CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { Execute.Assertion .ForCondition(accessModifier != Subject.GetCSharpAccessModifier()) .BecauseOf(because, becauseArgs) .FailWith("Expected method " + Subject.Name + " not to be {0}{reason}, but it is.", accessModifier); return(new AndConstraint <TAssertions>((TAssertions)this)); }
/// <summary> /// Asserts that the selected type has the specified C# <paramref name="accessModifier"/>. /// </summary> /// <param name="accessModifier">The expected C# access modifier.</param> /// <param name="because"> /// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion /// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically. /// </param> /// <param name="becauseArgs"> /// Zero or more objects to format using the placeholders in <see cref="because" />. /// </param> public AndConstraint <Type> HaveAccessModifier( CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { Execute.Assertion.ForCondition(accessModifier == Subject.GetCSharpAccessModifier()) .BecauseOf(because, becauseArgs) .FailWith("Expected type " + Subject.Name + " to be {0}{reason}, but it is {1}.", accessModifier, Subject.GetCSharpAccessModifier()); return(new AndConstraint <Type>(Subject)); }
/// <summary> /// Asserts that the selected methods don't have specified <paramref name="accessModifier"/> /// </summary> /// <param name="accessModifier">The expected access modifier.</param> /// <param name="because"> /// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion /// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically. /// </param> /// <param name="becauseArgs"> /// Zero or more objects to format using the placeholders in <paramref name="because" />. /// </param> public AndConstraint <MethodInfoSelectorAssertions> NotBe(CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { var methods = SubjectMethods.Where(pi => pi.GetCSharpAccessModifier() == accessModifier).ToArray(); var message = $"Expected all selected methods to not be {accessModifier}{{reason}}, but the following methods are:" + Environment.NewLine + GetDescriptionsFor(methods); Execute.Assertion .ForCondition(!methods.Any()) .BecauseOf(because, becauseArgs) .FailWith(message); return(new AndConstraint <MethodInfoSelectorAssertions>(this)); }
public static void Run(Options options) { string assemblyFile = options.AssemblyFilePath; Component component = options.Component; string outputDirectory = options.OutputDirectory; string @namespace = options.Namespace; CSharpAccessModifier accessModifier = options.AccessModifier; if (!File.Exists(assemblyFile)) { throw new ArgumentException($"Assembly file '{assemblyFile}' does not exist.", nameof(assemblyFile)); } if (string.IsNullOrWhiteSpace(@namespace)) { throw new ArgumentException("The provided namespace was empty.", nameof(@namespace)); } // Load the assembly which contains the interfaces to generate from Assembly assembly = Assembly.LoadFrom(assemblyFile); // Create the generator Generator generator = component switch { Component.Transmitter => new TransmitterGenerator(), Component.Receiver => new ReceiverGenerator(), _ => throw new ArgumentException("Unknown Decoupler component type.", nameof(Options.Component)) }; // Create the output directory if it doesn't exist if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } // Iterate over each interface, generating the output foreach (Type @interface in GetInterfaces(assembly)) { // Build the contract definition from the interface ContractDefinition contract = InterfaceContractDefinitionBuilder.BuildContract(@interface); // Create the names for the transmitter and receiver classes string className = $"{component}_{@interface.GetCSharpName(identifierOnly: true, includeNamespace: false)}"; // Generate the transmitter code string code = generator.Run(contract, className, @namespace, accessModifier); string outputFilePath = Path.Combine(outputDirectory, $"{className}.generated.cs"); File.WriteAllText(outputFilePath, code); } }
public static string ToCSharpString(this CSharpAccessModifier accessModifier) { switch (accessModifier) { case CSharpAccessModifier.Public: return("public"); case CSharpAccessModifier.Protected: return("protected"); case CSharpAccessModifier.Internal: return("internal"); case CSharpAccessModifier.Private: return("private"); default: throw new ArgumentException("Unknown access modifier", nameof(accessModifier)); } }
/// <inheritdoc /> protected override CSharpFile GenerateCode( ContractDefinition contract, string implementationName, string @namespace, CSharpAccessModifier accessLevel = CSharpAccessModifier.Public) { // Generate the code CSharpFile file = new CSharpFile( @namespace: @namespace, usings: null, classes: this.GetClasses(contract, implementationName, accessLevel), fileHeader: this.GetFileHeader(contract) ); return(file); }
/// <summary> /// Asserts that the selected property has a getter with the specified C# access modifier. /// </summary> /// <param name="accessModifier">The expected C# access modifier.</param> /// <param name="because"> /// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion /// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically. /// </param> /// <param name="becauseArgs"> /// Zero or more objects to format using the placeholders in <paramref name="because" />. /// </param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="accessModifier"/> /// is not a <see cref="CSharpAccessModifier"/> value.</exception> public AndConstraint <PropertyInfoAssertions> BeReadable(CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsOutOfRange(accessModifier, nameof(accessModifier)); bool success = Execute.Assertion .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith($"Expected {Identifier} to be {accessModifier}{{reason}}, but {{context:property}} is <null>."); if (success) { Subject.Should().BeReadable(because, becauseArgs); Subject.GetGetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); } return(new AndConstraint <PropertyInfoAssertions>(this)); }
/// <summary> /// Constructs a new <see cref="CSharpMethod" /> object. /// </summary> /// <param name="name">The name of the method.</param> /// <param name="returnType"> /// The full name of the method's return type. /// <para> /// If null, the return type will not be shown in the generated output. /// </para> /// </param> /// <param name="body">The method body.</param> /// <param name="parameters">The parameters which need to be provided as inputs to this method.</param> /// <param name="accessModifier">The access level of the method.</param> /// <param name="isStatic">True if this method should be marked as static, otherwise false.</param> /// <param name="isOverride">True if this method is overriding a base type's method, otherwise false.</param> /// <param name="isAsync">True if this method should be marked as async, otherwise false.</param> /// <param name="documentationComment">The method's documentation comment. The parameters' documentation will automatically be included in this comment.</param> public CSharpMethod( string name, string returnType, string body, IEnumerable <CSharpParameter> parameters = null, CSharpAccessModifier accessModifier = CSharpAccessModifier.Public, bool isStatic = false, bool isOverride = false, bool isAsync = false, CSharpDocumentationComment documentationComment = null) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Method name cannot be null or whitespace", nameof(name)); } if (returnType != null && string.IsNullOrWhiteSpace(returnType)) { throw new ArgumentException("Return type cannot be empty or whitespace", nameof(name)); } this.Name = name; this.AccessModifier = accessModifier; this.IsStatic = isStatic; this.IsOverride = isOverride; this.IsAsync = isAsync; this.ReturnType = returnType; this.Parameters = parameters ?? Array.Empty <CSharpParameter>(); this.Body = body ?? string.Empty; this.DocumentationComment = documentationComment; // Add parameter descriptions into the documentation comment string parametersDocumentationComment = this.GetParametersDocumentationComment(this.Parameters); if (!string.IsNullOrWhiteSpace(parametersDocumentationComment)) { if (!string.IsNullOrWhiteSpace(this.DocumentationComment?.RawNotes)) { parametersDocumentationComment += $"{Environment.NewLine}{this.DocumentationComment.RawNotes}"; } this.DocumentationComment = new CSharpDocumentationComment(this.DocumentationComment?.Summary, parametersDocumentationComment); } }
/// <summary> /// Constructs a new <see cref="CSharpClassConstructor" /> object. /// </summary> /// <param name="className">The name of the class that contains this constructor.</param> /// <param name="accessModifier">The access level of the method.</param> /// <param name="parameters">The parameters which need to be provided as inputs to this method.</param> /// <param name="baseClassConstructorParameterValues"> /// The parameter values to pass to the base class' constructor, as they will appear in the code. /// <para> /// If this is null, the base constructor call will not be visible in the generated output. /// Do this either when this constructor's class does not inherit from another class, or /// when the constructor should call the base class' default constructor automatically. /// </para> /// <para> /// NOTE: These values will not automatically be sanitized if they are identifiers. For /// example, if one of the variables being passed to the base constructor is called "class", /// then you should provide the string "@class" instead). /// </para> /// </param> /// <param name="body">The method body.</param> /// <param name="documentationComment"> /// The method's documentation comment. The parameters' documentation will automatically be /// included in this comment. /// </param> public CSharpClassConstructor( string className, string body, CSharpAccessModifier accessModifier = CSharpAccessModifier.Public, IEnumerable <CSharpParameter> parameters = null, IEnumerable <string> baseClassConstructorParameterValues = null, CSharpDocumentationComment documentationComment = null) : base( name: className, returnType: null, body: body, parameters: parameters, accessModifier: accessModifier, isStatic: false, isOverride: false, isAsync: false, documentationComment: documentationComment) { this.BaseClassConstructorParameterValues = baseClassConstructorParameterValues ?? Array.Empty <string>(); }
/// <summary> /// Generates the transmitter class. /// </summary> /// <param name="contract">The contract for which to generate the transmitter class.</param> /// <param name="implementationName">The name of the ouput class. This will also be the name of the output file before appending the extension.</param> /// <param name="namespace">The namespace in which to put the generated classes.</param> /// <param name="accessLevel">The access level of the generated classes.</param> public string Run( ContractDefinition contract, string implementationName, string @namespace, CSharpAccessModifier accessLevel = CSharpAccessModifier.Public) { if (contract == null) { throw new ArgumentNullException(nameof(contract)); } if (implementationName == null) { throw new ArgumentNullException(nameof(implementationName)); } if (!CSharpNamingUtils.IsValidIdentifier(implementationName)) { throw new ArgumentException($"Implementation name must be a valid class name: {implementationName}", nameof(implementationName)); } if (@namespace == null) { throw new ArgumentNullException(nameof(@namespace)); } foreach (string namespacePart in @namespace.Split('.')) { if (!CSharpNamingUtils.IsValidIdentifier(namespacePart)) { throw new ArgumentException($"The provided namespace is invalid because the part '{namespacePart}' is not a valid C# identifier: {@namespace}", nameof(@namespace)); } } // Validate the contract this.ValidateContract(contract); // Generate the code CSharpFile file = this.GenerateCode(contract, implementationName, @namespace, accessLevel); string result = file.ToString(); return(result); }
/// <summary> /// Asserts that the selected member does not have the specified C# <paramref name="accessModifier"/>. /// </summary> /// <param name="accessModifier">The unexpected C# access modifier.</param> /// <param name="because"> /// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion /// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically. /// </param> /// <param name="becauseArgs"> /// Zero or more objects to format using the placeholders in <paramref name="because" />. /// </param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="accessModifier"/> /// is not a <see cref="CSharpAccessModifier"/> value.</exception> public AndConstraint <TAssertions> NotHaveAccessModifier(CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsOutOfRange(accessModifier, nameof(accessModifier)); bool success = Execute.Assertion .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith($"Expected method not to be {accessModifier}{{reason}}, but {{context:member}} is <null>."); if (success) { CSharpAccessModifier subjectAccessModifier = Subject.GetCSharpAccessModifier(); Execute.Assertion .ForCondition(accessModifier != subjectAccessModifier) .BecauseOf(because, becauseArgs) .FailWith($"Expected method {Subject.Name} not to be {accessModifier}{{reason}}, but it is."); } return(new AndConstraint <TAssertions>((TAssertions)this)); }
/// <summary> /// Creates a new <see cref="CSharpClass" /> object. /// </summary> /// <param name="name">The name of the class.</param> /// <param name="accessModifier">The class' access modifier.</param> /// <param name="baseType">The class' base (super/parent) type.</param> /// <param name="interfaces">The interfaces that this class implements.</param> /// <param name="attributes">The attributes on this class.</param> /// <param name="properties">The properties in this class.</param> /// <param name="constructors">The constructor methods for this class.</param> /// <param name="methods">The methods in this class.</param> /// <param name="isStatic">True if the class should be marked as static, otherwise false.</param> /// <param name="documentationComment">The documentation comment on this class.</param> public CSharpClass( string name, CSharpAccessModifier accessModifier = CSharpAccessModifier.Public, IEnumerable <CSharpProperty> properties = null, IEnumerable <CSharpClassConstructor> constructors = null, IEnumerable <CSharpMethod> methods = null, bool isStatic = false, string baseType = null, IEnumerable <string> interfaces = null, IEnumerable <CSharpAttribute> attributes = null, CSharpDocumentationComment documentationComment = null) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Class name cannot be null or whitespace", nameof(name)); } if (baseType != null && string.IsNullOrWhiteSpace(baseType)) { throw new ArgumentException("Base type name cannot be empty. Set it to null to remove the base type.", nameof(baseType)); } this.Name = name; this.AccessModifier = accessModifier; this.BaseType = baseType; this.IsStatic = isStatic; this.Interfaces = interfaces == null ? Array.Empty <string>().AsEnumerable() : new HashSet <string>(interfaces); this.Attributes = attributes ?? Array.Empty <CSharpAttribute>(); this.Properties = properties ?? Array.Empty <CSharpProperty>(); this.Constructors = constructors ?? Array.Empty <CSharpClassConstructor>(); this.Methods = methods ?? Array.Empty <CSharpMethod>(); this.DocumentationComment = documentationComment; }
/// <summary> /// Checks if the subject info getter does not have the given access modifier. /// </summary> /// <param name="subjectInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info getter should not have.</param> /// <returns>True if the subject info getter does not have the given access modifier, false otherwise.</returns> public static bool WhichGetterDoesNotHave(this ISubjectInfo subjectInfo, CSharpAccessModifier accessModifier) { return subjectInfo.SelectedMemberInfo.GetAccessModifier != accessModifier; }
/// <summary> /// Checks if the subject info setter has the given access modifier. /// </summary> /// <param name="memberInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info setter should have.</param> /// <returns>True if the subject info setter has the given access modifier, false otherwise.</returns> public static bool WhichSetterHas(this IMemberInfo memberInfo, CSharpAccessModifier accessModifier) { return(memberInfo.SelectedMemberInfo.GetSetAccessModifier() == accessModifier); }
/// <summary> /// Generates code using the given contract. /// </summary> /// <param name="contract">The contract to generate code from.</param> /// <param name="implementationName">The name of the implementation.</param> /// <param name="namespace">The namespace in which to put the generated classes.</param> /// <param name="accessLevel">The access level of the generated classes.</param> /// <returns>The generated code.</returns> protected abstract CSharpFile GenerateCode(ContractDefinition contract, string implementationName, string @namespace, CSharpAccessModifier accessLevel);
/// <summary> /// Checks if the subject info getter does not have the given access modifier. /// </summary> /// <param name="subjectInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info getter should not have.</param> /// <returns>True if the subject info getter does not have the given access modifier, false otherwise.</returns> public static bool WhichGetterDoesNotHave(this ISubjectInfo subjectInfo, CSharpAccessModifier accessModifier) { return(subjectInfo.SelectedMemberInfo.GetAccessModifier != accessModifier); }
/// <summary> /// Checks if the subject info setter has the given access modifier. /// </summary> /// <param name="subjectInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info setter should have.</param> /// <returns>True if the subject info setter has the given access modifier, false otherwise.</returns> public static bool WhichSetterHas(this ISubjectInfo subjectInfo, CSharpAccessModifier accessModifier) { return(subjectInfo.SelectedMemberInfo.SetAccessModifier == accessModifier); }
private IEnumerable <CSharpClass> GetClasses(ContractDefinition contract, string implementationName, CSharpAccessModifier accessLevel) { Type baseClass = typeof(Transmitter); CSharpClass @class = new CSharpClass( name: implementationName, accessModifier: accessLevel, constructors: this.GetConstructors(implementationName), methods: this.GetMethods(contract.Operations), baseType: baseClass.GetCSharpName(), interfaces: contract.FullName.SingleObjectAsEnumerable(), documentationComment: !string.IsNullOrWhiteSpace(contract.Description) ? new CSharpDocumentationComment(summary: null, rawNotes: contract.Description) : null); yield return(@class); }
/// <summary> /// Checks if the subject info getter does not have the given access modifier. /// </summary> /// <param name="memberInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info getter should not have.</param> /// <returns>True if the subject info getter does not have the given access modifier, false otherwise.</returns> public static bool WhichGetterDoesNotHave(this IMemberInfo memberInfo, CSharpAccessModifier accessModifier) { return(memberInfo.GetterAccessibility != accessModifier); }
/// <summary> /// Checks if the subject info getter does not have the given access modifier. /// </summary> /// <param name="memberInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info getter should not have.</param> /// <returns>True if the subject info getter does not have the given access modifier, false otherwise.</returns> public static bool WhichGetterDoesNotHave(this IMemberInfo memberInfo, CSharpAccessModifier accessModifier) { return(memberInfo.SelectedMemberInfo.GetGetAccessModifier() != accessModifier); }
private IEnumerable <CSharpClass> GetClasses(ContractDefinition contract, string implementationName, CSharpAccessModifier accessLevel) { Type baseClass = typeof(Receiver <>); CSharpClass @class = new CSharpClass( name: implementationName, accessModifier: accessLevel, properties: this.GetProperties(contract), constructors: this.GetConstructors(contract, implementationName), methods: this.GetMethods(contract), baseType: $"{baseClass.GetCSharpName(identifierOnly: true)}<{contract.FullName}>", documentationComment: !string.IsNullOrWhiteSpace(contract.Description) ? new CSharpDocumentationComment(summary: null, rawNotes: contract.Description) : null); yield return(@class); }
/// <summary> /// Checks if the subject info getter has the given access modifier. /// </summary> /// <param name="memberInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info getter should have.</param> /// <returns>True if the subject info getter has the given access modifier, false otherwise.</returns> public static bool WhichGetterHas(this IMemberInfo memberInfo, CSharpAccessModifier accessModifier) { return(memberInfo.GetterAccessibility == accessModifier); }
/// <summary> /// Checks if the subject info setter has the given access modifier. /// </summary> /// <param name="subjectInfo">The subject info being checked.</param> /// <param name="accessModifier">The access modifier that the subject info setter should have.</param> /// <returns>True if the subject info setter has the given access modifier, false otherwise.</returns> public static bool WhichSetterHas(this ISubjectInfo subjectInfo, CSharpAccessModifier accessModifier) { return subjectInfo.SelectedMemberInfo.SetAccessModifier == accessModifier; }