using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using AngleSharp.Html.Parser; using Generator.Models; using Scriban; using Scriban.Runtime; using Spectre.Cli; using Spectre.IO; using Path = Spectre.IO.Path; using SpectreEnvironment = Spectre.IO.Environment; namespace Generator.Commands { public sealed class EmojiGeneratorCommand : AsyncCommand { private readonly IFileSystem _fileSystem; private readonly IEnvironment _environment; private readonly IHtmlParser _parser; private readonly Dictionary _templates = new Dictionary { { "Templates/Emoji.Generated.template", "Emoji.Generated.cs" }, { "Templates/Emoji.Json.template", "emojis.json" }, // For documentation }; public sealed class Settings : GeneratorSettings { [CommandOption("-i|--input ")] public string Input { get; set; } } public EmojiGeneratorCommand() { _fileSystem = new FileSystem(); _environment = new SpectreEnvironment(); _parser = new HtmlParser(); } public override async Task ExecuteAsync(CommandContext context, Settings settings) { var output = new DirectoryPath(settings.Output); if (!_fileSystem.Directory.Exists(settings.Output)) { _fileSystem.Directory.Create(settings.Output); } var stream = await FetchEmojis(settings); var document = await _parser.ParseDocumentAsync(stream); var emojis = Emoji.Parse(document).OrderBy(x => x.Name) .Where(emoji => !emoji.HasCombinators) .ToList(); // Render all templates foreach (var (templateFilename, outputFilename) in _templates) { var result = await RenderTemplate(new FilePath(templateFilename), emojis); var outputPath = output.CombineWithFilePath(outputFilename); await File.WriteAllTextAsync(outputPath.FullPath, result); } return 0; } private async Task FetchEmojis(Settings settings) { var input = string.IsNullOrEmpty(settings.Input) ? _environment.WorkingDirectory : new DirectoryPath(settings.Input); var file = _fileSystem.File.Retrieve(input.CombineWithFilePath("emoji-list.html")); if (!file.Exists) { using var http = new HttpClient(); using var httpStream = await http.GetStreamAsync("http://www.unicode.org/emoji/charts/emoji-list.html"); using var outStream = file.OpenWrite(); await httpStream.CopyToAsync(outStream); } return file.OpenRead(); } private static async Task RenderTemplate(Path path, IReadOnlyCollection emojis) { var text = await File.ReadAllTextAsync(path.FullPath); var template = Template.Parse(text); var templateContext = new TemplateContext { // Because of the insane amount of Emojis, // we need to get rid of some secure defaults :P LoopLimit = int.MaxValue, }; var scriptObject = new ScriptObject(); scriptObject.Import(new { Emojis = emojis }); templateContext.PushGlobal(scriptObject); return await template.RenderAsync(templateContext); } } }