/// <summary>
        /// Create cil source-code representation of a <see cref="EnumDefinition"/>.
        /// </summary>
        /// <exception cref="Exceptions.InvalidAssemblyNameException">
        /// Thrown when a invalid assembly name is given.
        /// </exception>
        /// <exception cref="Exceptions.InvalidNamespaceException">
        /// Thrown when a invalid namespace identifier is given.
        /// </exception>
        /// <exception cref="Exceptions.OutOfBoundsValueException">
        /// Thrown when enum value does not fit in given storage-type.
        /// </exception>
        /// <param name="enumDefinition">Enum to generate cil source-code for</param>
        /// <param name="assemblyName">Name of the assembly to generate</param>
        /// <param name="namespace">Optional namespace to add the enum to</param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentMode">Mode to use for indenting</param>
        /// <param name="spaceIndentSize">When indenting with spaces this controls how many</param>
        /// <param name="newlineMode">Mode to use for ending lines</param>
        /// <param name="storageType">Underlying enum storage-type to use</param>
        /// <param name="curlyBracketMode">Mode to use when writing curly brackets</param>
        /// <returns>String containing the genenerated cil sourcecode</returns>
        public static string ExportCil(
            this EnumDefinition enumDefinition,
            string assemblyName,
            string @namespace                   = null,
            HeaderMode headerMode               = HeaderMode.Default,
            CodeBuilder.IndentMode indentMode   = CodeBuilder.IndentMode.Spaces,
            int spaceIndentSize                 = 4,
            CodeBuilder.NewlineMode newlineMode = CodeBuilder.NewlineMode.Unix,
            StorageType storageType             = StorageType.Implicit,
            CurlyBracketMode curlyBracketMode   = CurlyBracketMode.NewLine)
        {
            if (enumDefinition == null)
            {
                throw new ArgumentNullException(nameof(enumDefinition));
            }

            if (string.IsNullOrEmpty(assemblyName) || !IdentifierValidator.Validate(assemblyName))
            {
                throw new InvalidAssemblyNameException(assemblyName);
            }

            if (!string.IsNullOrEmpty(@namespace) && !IdentifierValidator.ValidateNamespace(@namespace))
            {
                throw new InvalidNamespaceException(@namespace);
            }

            foreach (var oobEntry in enumDefinition.Entries.Where(e => !storageType.Validate(e.Value)))
            {
                throw new OutOfBoundsValueException(storageType, oobEntry.Value);
            }

            var builder = new CodeBuilder(indentMode, spaceIndentSize, newlineMode);

            if (headerMode != HeaderMode.None)
            {
                builder.AddHeader();
                builder.WriteEndLine();
            }

            // Add reference to mscorlib.
            builder.WriteLine(".assembly extern mscorlib { }");
            builder.WriteEndLine();

            // Add assembly info.
            builder.Write($".assembly {assemblyName}");
            StartScope(builder, curlyBracketMode);
            builder.WriteLine(".ver 1:0:0:0");
            EndScope(builder);
            builder.WriteEndLine();

            // Add module info.
            builder.WriteLine($".module {assemblyName}.dll");
            builder.WriteEndLine();

            // Add enum class.
            builder.AddEnum(enumDefinition, storageType, curlyBracketMode, @namespace);

            return(builder.Build());
        }
        /// <summary>
        /// Create csharp source-code representation of a <see cref="EnumDefinition"/>.
        /// </summary>
        /// <exception cref="Exceptions.InvalidNamespaceException">
        /// Thrown when a invalid namespace identifier is given.
        /// </exception>
        /// <exception cref="Exceptions.OutOfBoundsValueException">
        /// Thrown when enum value does not fit in given storage-type.
        /// </exception>
        /// <param name="enumDefinition">Enum to generate csharp source-code for</param>
        /// <param name="namespace">Optional namespace to add the enum to</param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentMode">Mode to use for indenting</param>
        /// <param name="spaceIndentSize">When indenting with spaces this controls how many</param>
        /// <param name="newlineMode">Mode to use for ending lines</param>
        /// <param name="storageType">Underlying enum storage-type to use</param>
        /// <param name="curlyBracketMode">Mode to use when writing curly brackets</param>
        /// <returns>String containing the genenerated csharp sourcecode</returns>
        public static string ExportCSharp(
            this EnumDefinition enumDefinition,
            string @namespace                   = null,
            HeaderMode headerMode               = HeaderMode.Default,
            CodeBuilder.IndentMode indentMode   = CodeBuilder.IndentMode.Spaces,
            int spaceIndentSize                 = 4,
            CodeBuilder.NewlineMode newlineMode = CodeBuilder.NewlineMode.Unix,
            StorageType storageType             = StorageType.Implicit,
            CurlyBracketMode curlyBracketMode   = CurlyBracketMode.NewLine)
        {
            if (enumDefinition == null)
            {
                throw new ArgumentNullException(nameof(enumDefinition));
            }

            if (!string.IsNullOrEmpty(@namespace) && !IdentifierValidator.ValidateNamespace(@namespace))
            {
                throw new InvalidNamespaceException(@namespace);
            }

            foreach (var oobEntry in enumDefinition.Entries.Where(e => !storageType.Validate(e.Value)))
            {
                throw new OutOfBoundsValueException(storageType, oobEntry.Value);
            }

            var builder = new CodeBuilder(indentMode, spaceIndentSize, newlineMode);

            if (headerMode != HeaderMode.None)
            {
                builder.AddHeader();
                builder.WriteEndLine();
            }

            builder.WriteLine("using System.CodeDom.Compiler;");
            builder.WriteEndLine();

            if (string.IsNullOrEmpty(@namespace))
            {
                builder.AddEnum(enumDefinition, storageType, curlyBracketMode);
            }
            else
            {
                builder.AddNamespace(
                    @namespace,
                    b => b.AddEnum(enumDefinition, storageType, curlyBracketMode),
                    curlyBracketMode);
            }

            return(builder.Build());
        }
        /// <summary>
        /// Run the generator tool.
        /// </summary>
        /// <param name="inputFile">Path to the json file to generate the enum for</param>
        /// <param name="outputFile">Path where to save the generated enum</param>
        /// <param name="outputType">Type of output file to generate</param>
        /// <param name="collectionJPath">JPath to the collection in the input file</param>
        /// <param name="entryNameJPath">
        /// JPath to the name field in an entry in the input file</param>
        /// <param name="entryValueJPath">
        /// Optional JPath to the value field in an entry in the input file
        /// </param>
        /// <param name="entryCommentJPath">
        /// Optional JPath to the comment field in an entry in the input file
        /// </param>
        /// <param name="enumComment">
        /// Optional comment to add to the generated enum.
        /// </param>
        /// <param name="enumNamespace">
        /// Optional namespace to add the generated enum to.
        /// </param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentMode">Mode to use when indenting text</param>
        /// <param name="indentSize">When indenting with spaces this controls how many</param>
        /// <param name="newlineMode">Mode to use when adding newlines to text</param>
        /// <param name="storageType">Storage type for the exported enum</param>
        /// <param name="curlyBracketMode">Mode to use when writing curly-brackets</param>
        /// <returns>Exit code</returns>
        public int Run(
            string inputFile,
            string outputFile,
            OutputType outputType,
            string collectionJPath,
            string entryNameJPath,
            string entryValueJPath,
            string entryCommentJPath,
            string enumComment,
            string enumNamespace,
            Core.Exporter.HeaderMode headerMode,
            CodeBuilder.IndentMode indentMode,
            int indentSize,
            CodeBuilder.NewlineMode newlineMode,
            Core.Exporter.StorageType storageType,
            Core.Exporter.CurlyBracketMode curlyBracketMode)
        {
            if (string.IsNullOrEmpty(inputFile))
            {
                throw new ArgumentException($"Invalid path: '{inputFile}'", nameof(inputFile));
            }
            if (string.IsNullOrEmpty(outputFile))
            {
                throw new ArgumentException($"Invalid path: '{outputFile}'", nameof(outputFile));
            }
            if (string.IsNullOrEmpty(collectionJPath))
            {
                throw new ArgumentException($"Invalid JPath: '{collectionJPath}'", nameof(collectionJPath));
            }
            if (string.IsNullOrEmpty(entryNameJPath))
            {
                throw new ArgumentException($"Invalid JPath: '{entryNameJPath}'", nameof(entryNameJPath));
            }

            // Resolve relative paths to absolute paths.
            var fullInputPath  = this.GetFullPath(inputFile);
            var fullOutputPath = this.GetFullPath(outputFile);

            if (fullInputPath == null || fullOutputPath == null)
            {
                return(1);
            }

            // Get input.
            var inputJson = GetInputJson();

            if (inputJson == null)
            {
                return(1);
            }

            // Generate enum name.
            var enumName = GetEnumName();

            if (enumName == null)
            {
                return(1);
            }

            // Create mapping context.
            var context = Context.Create(
                collectionJPath,
                entryNameJPath,
                entryValueJPath,
                entryCommentJPath,
                this.logger);

            // Map enum.
            EnumDefinition enumDefinition = null;

            try
            {
                enumDefinition = context.MapEnum(inputJson, enumName, enumComment);
            }
            catch (JsonParsingFailureException)
            {
                this.logger.LogCritical("Failed to parse input file: invalid json");
                return(1);
            }
            catch (MappingFailureException e)
            {
                this.logger.LogCritical($"Failed to map enum: {e.InnerException.Message}");
                return(1);
            }

            // Export.
            var output = Export(enumDefinition);

            if (output == null)
            {
                return(1);
            }

            // Save to file.
            try
            {
                if (!fullOutputPath.EndsWith(GetRequiredExtension(outputType), StringComparison.OrdinalIgnoreCase))
                {
                    fullOutputPath = $"{fullOutputPath}{GetDesiredExtension(outputType)}";
                }
                Directory.GetParent(fullOutputPath).Create();
                using (var stream = new FileStream(fullOutputPath, FileMode.Create, FileAccess.Write))
                {
                    stream.Write(output);
                }

                this.logger.LogInformation($"Written enum to: '{fullOutputPath}'");
            }
            catch (Exception e)
            {
                this.logger.LogCritical($"Failed to save to '{fullOutputPath}': {e.Message}");
                return(1);
            }

            return(0);

            byte[] Export(EnumDefinition enumDef)
            {
                switch (outputType)
                {
                case OutputType.CSharp:
                    try
                    {
                        return(Utf8NoBom.GetBytes(enumDef.ExportCSharp(
                                                      enumNamespace,
                                                      headerMode,
                                                      indentMode,
                                                      indentSize,
                                                      newlineMode,
                                                      storageType,
                                                      curlyBracketMode)));
                    }
                    catch (Exception e)
                    {
                        this.logger.LogCritical($"Failed to generate csharp: {e.Message}");
                        return(null);
                    }

                case OutputType.FSharp:
                    try
                    {
                        return(Utf8NoBom.GetBytes(enumDef.ExportFSharp(
                                                      string.IsNullOrEmpty(enumNamespace) ? "Generated" : enumNamespace,
                                                      headerMode,
                                                      indentSize,
                                                      newlineMode,
                                                      storageType)));
                    }
                    catch (Exception e)
                    {
                        this.logger.LogCritical($"Failed to generate fsharp: {e.Message}");
                        return(null);
                    }

                case OutputType.VisualBasic:
                    try
                    {
                        return(Utf8NoBom.GetBytes(enumDef.ExportVisualBasic(
                                                      enumNamespace,
                                                      headerMode,
                                                      indentMode,
                                                      indentSize,
                                                      newlineMode,
                                                      storageType)));
                    }
                    catch (Exception e)
                    {
                        this.logger.LogCritical($"Failed to generate csharp: {e.Message}");
                        return(null);
                    }

                case OutputType.Cil:
                    try
                    {
                        return(Utf8NoBom.GetBytes(enumDef.ExportCil(
                                                      assemblyName: enumName,
                                                      enumNamespace,
                                                      headerMode,
                                                      indentMode,
                                                      indentSize,
                                                      newlineMode,
                                                      storageType,
                                                      curlyBracketMode)));
                    }
                    catch (Exception e)
                    {
                        this.logger.LogCritical($"Failed to generate cil: {e.Message}");
                        return(null);
                    }

                case OutputType.ClassLibrary:
                    try
                    {
                        return(enumDef.ExportClassLibrary(
                                   assemblyName: enumName,
                                   enumNamespace,
                                   storageType));
                    }
                    catch (Exception e)
                    {
                        this.logger.LogCritical($"Failed to generate class-library: {e.Message}");
                        return(null);
                    }

                default:
                    this.logger.LogCritical($"Unsupported output-type '{outputType}'");
                    return(null);
                }
            }

            string GetInputJson()
            {
                if (!File.Exists(fullInputPath))
                {
                    this.logger.LogCritical($"No file found at: '{fullInputPath}'");
                    return(null);
                }

                try
                {
                    var result = File.ReadAllText(fullInputPath);
                    this.logger.LogDebug($"'{result.Length}' characters read from: '{fullInputPath}'");
                    return(result);
                }
                catch (Exception e)
                {
                    this.logger.LogCritical($"Unable to read file '{fullInputPath}': {e.Message}");
                    return(null);
                }
            }

            string GetEnumName()
            {
                // Determine file-name.
                string fileName;

                try
                {
                    fileName = Path.GetFileNameWithoutExtension(fullOutputPath);
                    if (fileName.EndsWith(".g", StringComparison.OrdinalIgnoreCase))
                    {
                        fileName = fileName.Substring(0, fileName.Length - 2);
                    }
                }
                catch (Exception)
                {
                    this.logger.LogCritical($"Unable to get file-name from path: '{fullOutputPath}'");
                    return(null);
                }

                // Convert file-name into valid identifier.
                if (IdentifierCreator.TryCreateIdentifier(fileName, out var nameId))
                {
                    this.logger.LogDebug($"Generated enum-name: '{nameId}' from file-name: '{fileName}'");
                    return(nameId);
                }

                this.logger.LogCritical($"Unable to create valid identifier from file-name: '{fileName}'");
                return(null);
            }
        }
Пример #4
0
        /// <summary>
        /// Generate enum file.
        /// </summary>
        /// <param name="inputJsonText">Input json text to generate the enum from</param>
        /// <param name="outputPath">Path where to save the generated enum to</param>
        /// <param name="outputType">Type of output to produce</param>
        /// <param name="collectionJPath">JPath to the collection in the input file</param>
        /// <param name="entryNameJPath">
        /// JPath to the name field in an entry in the input file</param>
        /// <param name="entryValueJPath">
        /// Optional JPath to the value field in an entry in the input file.
        /// </param>
        /// <param name="entryCommentJPath">
        /// Optional JPath to the comment field in an entry in the input file.
        /// </param>
        /// <param name="enumComment">
        /// Optional comment to add to the generated enum.
        /// </param>
        /// <param name="enumNamespace">
        /// Optional namespace to add the generated enum to.
        /// </param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentMode">Mode to use when indenting text</param>
        /// <param name="indentSize">When indenting with spaces this controls how many</param>
        /// <param name="newlineMode">Mode to use when adding newlines to text</param>
        /// <param name="storageType">Storage type for the exported enum</param>
        /// <param name="curlyBracketMode">Mode to use when writing curly-brackets</param>
        /// <param name="logger">Optional logger for diagnostic output</param>
        public static void GenerateEnumToFile(
            string inputJsonText,
            string outputPath,
            OutputType outputType,
            string collectionJPath,
            string entryNameJPath,
            string entryValueJPath,
            string entryCommentJPath,
            string enumComment,
            string enumNamespace,
            HeaderMode headerMode,
            CodeBuilder.IndentMode indentMode,
            int indentSize,
            CodeBuilder.NewlineMode newlineMode,
            StorageType storageType,
            CurlyBracketMode curlyBracketMode,
            ILogger logger = null)
        {
            // Generate enum name.
            var enumName = GetEnumName(outputPath, logger);

            if (enumName == null)
            {
                return;
            }

            // Create mapping context.
            var context = Context.Create(
                collectionJPath,
                entryNameJPath,
                entryValueJPath,
                entryCommentJPath,
                logger);

            // Map enum.
            EnumDefinition enumDefinition = null;

            try
            {
                enumDefinition = context.MapEnum(inputJsonText, enumName, enumComment);
            }
            catch (JsonParsingFailureException)
            {
                logger?.LogCritical("Failed to parse input file: invalid json");
                return;
            }
            catch (MappingFailureException e)
            {
                logger?.LogCritical($"Failed to map enum: {e.InnerException.Message}");
                return;
            }

            // Export.
            byte[] output = null;
            switch (outputType)
            {
            case OutputType.CSharp:
                try
                {
                    output = Utf8NoBom.GetBytes(enumDefinition.ExportCSharp(
                                                    enumNamespace,
                                                    headerMode,
                                                    indentMode,
                                                    indentSize,
                                                    newlineMode,
                                                    storageType,
                                                    curlyBracketMode));
                }
                catch (Exception e)
                {
                    logger?.LogCritical($"Failed to generate csharp: {e.Message}");
                    return;
                }

                break;

            case OutputType.FSharp:
                try
                {
                    output = Utf8NoBom.GetBytes(enumDefinition.ExportFSharp(
                                                    string.IsNullOrEmpty(enumNamespace) ? "Generated" : enumNamespace,
                                                    headerMode,
                                                    indentSize,
                                                    newlineMode,
                                                    storageType));
                }
                catch (Exception e)
                {
                    logger?.LogCritical($"Failed to generate fsharp: {e.Message}");
                    return;
                }

                break;

            case OutputType.VisualBasic:
                try
                {
                    output = Utf8NoBom.GetBytes(enumDefinition.ExportVisualBasic(
                                                    enumNamespace,
                                                    headerMode,
                                                    indentMode,
                                                    indentSize,
                                                    newlineMode,
                                                    storageType));
                }
                catch (Exception e)
                {
                    logger?.LogCritical($"Failed to generate visual-basic: {e.Message}");
                    return;
                }

                break;

            case OutputType.Cil:
                try
                {
                    output = Utf8NoBom.GetBytes(enumDefinition.ExportCil(
                                                    assemblyName: enumName,
                                                    enumNamespace,
                                                    headerMode,
                                                    indentMode,
                                                    indentSize,
                                                    newlineMode,
                                                    storageType,
                                                    curlyBracketMode));
                }
                catch (Exception e)
                {
                    logger?.LogCritical($"Failed to generate cil: {e.Message}");
                    return;
                }

                break;

            case OutputType.ClassLibrary:
                try
                {
                    output = enumDefinition.ExportClassLibrary(
                        assemblyName: enumName,
                        enumNamespace,
                        storageType);
                }
                catch (Exception e)
                {
                    logger?.LogCritical($"Failed to generate classlibrary: {e.Message}");
                    return;
                }

                break;
            }

            // Write the file.
            try
            {
                var fullPath = Path.Combine(UnityEngine.Application.dataPath, outputPath);
                if (!fullPath.EndsWith(GetRequiredExtension(outputType), StringComparison.OrdinalIgnoreCase))
                {
                    fullPath = $"{fullPath}{GetDesiredExtension(outputType)}";
                }

                var outputDir = Path.GetDirectoryName(fullPath);
                if (!Directory.Exists(outputDir))
                {
                    logger?.LogDebug($"Creating output directory: '{outputDir}'");
                    Directory.CreateDirectory(outputDir);
                }

                File.WriteAllBytes(fullPath, output);
                logger?.LogInformation($"Saved enum: '{fullPath}'");
            }
            catch (Exception e)
            {
                logger?.LogCritical($"Failed to save enum: {e.Message}");
            }
        }
        /// <summary>
        /// Create fsharp source-code representation of a <see cref="EnumDefinition"/>.
        /// </summary>
        /// <exception cref="Exceptions.InvalidNamespaceException">
        /// Thrown when a invalid namespace identifier is given.
        /// </exception>
        /// <exception cref="Exceptions.OutOfBoundsValueException">
        /// Thrown when enum value does not fit in given storage-type.
        /// </exception>
        /// <param name="enumDefinition">Enum to generate fsharp source-code for</param>
        /// <param name="namespace">Namespace to add the enum to</param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentSize">How many spaces should be used for indents</param>
        /// <param name="newlineMode">Mode to use for ending lines</param>
        /// <param name="storageType">Underlying enum storage-type to use</param>
        /// <returns>String containing the genenerated fsharp sourcecode</returns>
        public static string ExportFSharp(
            this EnumDefinition enumDefinition,
            string @namespace,
            HeaderMode headerMode = HeaderMode.Default,
            int indentSize        = 4,
            CodeBuilder.NewlineMode newlineMode = CodeBuilder.NewlineMode.Unix,
            StorageType storageType             = StorageType.Implicit)
        {
            if (enumDefinition == null)
            {
                throw new ArgumentNullException(nameof(enumDefinition));
            }

            if (!IdentifierValidator.ValidateNamespace(@namespace))
            {
                throw new InvalidNamespaceException(@namespace);
            }

            foreach (var oobEntry in enumDefinition.Entries.Where(e => !storageType.Validate(e.Value)))
            {
                throw new OutOfBoundsValueException(storageType, oobEntry.Value);
            }

            var builder = new CodeBuilder(CodeBuilder.IndentMode.Spaces, indentSize, newlineMode);

            if (headerMode != HeaderMode.None)
            {
                builder.AddHeader();
                builder.WriteEndLine();
            }

            // Add namespace.
            builder.WriteLine($"namespace {@namespace}");
            builder.WriteEndLine();

            // Open System.CodeDom.Compiler (for the 'GeneratedCode' attribute)
            builder.WriteLine("open System.CodeDom.Compiler");
            builder.WriteEndLine();

            // Add type comment.
            if (!string.IsNullOrEmpty(enumDefinition.Comment))
            {
                builder.AddSummary(enumDefinition.Comment);
            }

            // Add enum definition.
            var assemblyName = typeof(FSharpExporter).Assembly.GetName();

            builder.WriteLine($"[<GeneratedCode(\"{assemblyName.Name}\", \"{assemblyName.Version}\")>]");
            builder.WriteLine($"type {enumDefinition.Identifier} =");

            // Add entries.
            foreach (var entry in enumDefinition.Entries)
            {
                var literalSuffix = storageType == StorageType.Implicit ?
                                    string.Empty :
                                    storageType.GetFSharpLiteralSuffix();

                builder.WriteLine($"| {entry.Name} = {entry.Value}{literalSuffix}", additionalIndent: 1);
            }

            return(builder.Build());
        }