using Serilog; using Tommy; namespace DownloadManager; class configuration { private record struct options(bool dryRun, bool confirm, string downloadDirectory, string logDirectory); /* Default locations for essential things arrays can be added to later by the user with the last valid entry being used.*/ private static readonly string _home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); private static readonly string _defaultConfigLocation = _home + "/.config/sherlock5512/downloadmanager"; private string[] _configLocations = { $"{_defaultConfigLocation}/config.toml" }; private string[] _ruleDirLocation = { $"{_defaultConfigLocation}/rules/" }; private static readonly options _defaultOptions = new options( dryRun: false, confirm: false, downloadDirectory: _home + "/Downloads", logDirectory: _home + "/.local/share/sherlock5512"); /* Options are exposed via properties */ private options _options; public bool dryRun { get => _options.dryRun; } public bool confirm { get => _options.confirm; } public string downloadDirectory { get => _options.downloadDirectory; } public string logDirectory { get => _options.logDirectory; } public string[] ruleDirectories /* Only returns directories that exist */ { get => _ruleDirLocation .Where(x => Directory.Exists(x)) .ToArray(); } public configuration() { _options = new(); Log.Information("new Configuration object created"); _options = loadConfig() ?? _options; /* Attempt to load config */ createDirs(); verifyConfig(); } /* When arguments are passed we use them and don't load a config*/ public configuration(bool? dryRun, bool? confirm, string? downloadDirectory, string? logDirectory) { _options = new( /* labels are used here to allow constructor signature changes */ dryRun: dryRun ?? _defaultOptions.dryRun, confirm: confirm ?? _defaultOptions.confirm, downloadDirectory: downloadDirectory ?? _defaultOptions.downloadDirectory, logDirectory: logDirectory ?? _defaultOptions.logDirectory ); createDirs(); verifyConfig(); } /* If the user specifies a config file on the command line */ public configuration(string configLocation) { _configLocations.Append(configLocation); var opt = loadConfig(); if (opt is null) { Log.Fatal("Could not load user provided config {loc}", configLocation); Environment.Exit(78); } _options = (options)opt; createDirs(); verifyConfig(); } private options? loadConfig() { string[] ValidLocations = _configLocations.Where(x => Path.Exists(x)).ToArray(); foreach (var location in ValidLocations) { Log.Information("Attempting to load config from {location}", location); try { Log.Debug("In try statement"); using (StreamReader reader = File.OpenText(location)) { TomlTable table = TOML.Parse(reader); Log.Debug("Parsed TOML to table: {@table}", table); // get our config values bool dryRun = table.HasKey("dryRun") ? table["dryRun"] : _defaultOptions.dryRun; bool confirm = table.HasKey("confirm") ? table["confirm"] : _defaultOptions.confirm; string downloadDirectory = table.HasKey("downloadDirectory") ? table["downloadDirectory"] : _defaultOptions.downloadDirectory; string logDirectory = table.HasKey("logDirectory") ? table["logDirectory"] : _defaultOptions.logDirectory; // construct the options object options opts = new( dryRun: dryRun, confirm: confirm, downloadDirectory: downloadDirectory, logDirectory: logDirectory ); Log.Debug("Constructed options object: {options}", opts); return opts; } } catch (TomlParseException e) { foreach (var syntaxEx in e.SyntaxErrors) { Log.Error("Toml error at l:{line} c:{col}: {message}", syntaxEx.Line, syntaxEx.Column, syntaxEx.Message); } } catch (Exception e) { Log.Fatal(e, "Unhandled error decoding toml {location}", location); Environment.Exit(78); } Log.Debug("After try catch block"); } Log.Error("Attempted to load all valid locations but no config could be loaded successfully"); return null; } private bool verifyConfig() { if (!Directory.Exists(_options.logDirectory)) { Log.Fatal("Log directory {dir} does not exist", logDirectory); Environment.Exit(78); } if (!Directory.Exists(_options.downloadDirectory)) { Log.Fatal("Downloads directory {dir} does not exist", downloadDirectory); Environment.Exit(78); } return true; } /* Attempt to create some directories if missing */ private bool createDirs() { try { Directory.CreateDirectory(_defaultConfigLocation); Directory.CreateDirectory(_options.logDirectory); } catch (UnauthorizedAccessException e) { Log.Warning(e, "Failed to create directory, This may cause other errors"); return false; } return true; } }