From ac1d3862c6a970856a82e62735a4e8b3f5ee9d87 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sun, 16 Mar 2025 11:33:45 -0400 Subject: [PATCH 1/3] Switch to Json (broken) --- .editorconfig | 128 +++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 1 + Config.cs | 6 +-- GlobalUsings.cs | 2 + Program.cs | 94 ++++++++++++++++++++++----------- README.md | 8 ++- Schedule.cs | 9 ++++ config.toml.sample | 2 +- 8 files changed, 213 insertions(+), 37 deletions(-) create mode 100644 .editorconfig create mode 100644 Schedule.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b4e4ee7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,128 @@ +# editorconfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +charset = utf-8 +end_of_line = crlf +indent_style = tab +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# only use var when it's obvious what the variable type is +csharp_style_var_for_built_in_types = true:none +csharp_style_var_when_type_is_apparent = true:none +csharp_style_var_elsewhere = true:suggestion + +# use language keywords instead of BCL types +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Use UPPER_CASE for private or internal constant fields +dotnet_naming_rule.constants_should_be_upper_case.severity = suggestion +dotnet_naming_rule.constants_should_be_upper_case.symbols = constants +dotnet_naming_rule.constants_should_be_upper_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = all_upper + +# Comment this group and uncomment out the next group if you don't want _ prefixed fields. + +# internal and private fields should be _camel_case +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style + +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal + +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code style defaults +dotnet_sort_system_directives_first = true +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = when_multiline:silent \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6136cef..cbc3037 100644 --- a/.gitignore +++ b/.gitignore @@ -546,3 +546,4 @@ FodyWeavers.xsd .idea/** *.txt *.toml +*.json \ No newline at end of file diff --git a/Config.cs b/Config.cs index fd092d4..933c122 100644 --- a/Config.cs +++ b/Config.cs @@ -10,15 +10,15 @@ public class Config /// /// Gets or sets the name of the schedule file. /// - public string? File { get; set; } + public string File { get; set; } = "schedule.txt"; /// /// Gets or sets the directory path where the schedule file is stored. /// - public string? Path { get; set; } + public string Path { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); /// /// Gets or sets the list of available topics from the configuration file. /// - public TomlArray? Topics { get; set; } + public IList Topics { get; set; } = new[] { "Games", "Politics", "Research", "Technology" }; } diff --git a/GlobalUsings.cs b/GlobalUsings.cs index 75ae6a1..6e12d2f 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -1,3 +1,5 @@ global using Tomlyn; global using Tomlyn.Model; global using PublishTimes; +global using System.Text.Json; +global using System.Text.Json.Serialization; \ No newline at end of file diff --git a/Program.cs b/Program.cs index f5a858b..2fdbc2c 100644 --- a/Program.cs +++ b/Program.cs @@ -1,5 +1,27 @@ // I hereby waive this project under the public domain - see UNLICENSE for details. +/// +/// Retrieves configuration settings from a TOML file if it exists; otherwise, returns a default configuration. +/// +/// The name of the configuration file (defaults to "config.toml"). +/// A Config object populated with values from the file, or a default Config instance if the file is not found. +Config GetConfig(string file = "config.toml") +{ + // App directory is used for config file + var appDir = AppDomain.CurrentDomain.BaseDirectory; + var cfgPath = Path.Combine(appDir, file); + + if (File.Exists(cfgPath)) + { + var toml = File.ReadAllText(cfgPath); + var model = Toml.ToModel(toml); + + return model; + } + + return new Config(); +} + /// /// Prompts the user with a yes/no question and returns their choice as a boolean value. /// @@ -90,7 +112,7 @@ string SelectTopics(List topics) } var selection = string.Join(", ", userChoices.ToArray()); - Console.WriteLine($"{Environment.NewLine}Select a Topic (Choose a Number){Environment.NewLine}{selection}"); + Console.WriteLine($"{Environment.NewLine}Select a Number{Environment.NewLine}{selection}"); var input = Console.ReadLine(); // Attempt to parse a number. @@ -114,7 +136,7 @@ string NewTopic(List topics) if (UserChoice("Choose a Topic?")) newTopic = SelectTopics(topics); else - newTopic = "==="; + newTopic = "Any"; return newTopic; } @@ -130,24 +152,24 @@ void ExportSchedule(List storeSchedule) var appDir = AppDomain.CurrentDomain.BaseDirectory; // File directory is used for file location set in config var outputDir = Directory.GetCurrentDirectory(); - var defaultTopics = new[] { "Games", "Politics", "Research", "Technology" }; - var outputFile = "schedule.txt"; var cfgFile = "config.toml"; + var topics = new List(); var cfgPath = Path.Combine(appDir, cfgFile); - var filePath = Path.Combine(outputDir, outputFile); - var appendSchedule = false; - var topic = ""; + var config = GetConfig(cfgPath); + var outputFile = config.File; + var filePath = Path.Combine(outputDir, outputFile!); + var chosenTopic = ""; + // If the config file exists, read from that but don't assume anything is filled if (File.Exists(cfgPath)) { var toml = File.ReadAllText(cfgPath); - var model = Toml.ToModel(toml); - var usrDir = model.Path; - var usrFileName = model.File; - var tomlList = string.Join(", ", model.Topics); - var usrList = tomlList.Split(','); + var usrDir = config.Path; + var usrFileName = config.File; + // Convert list into array + var usrTopics = config.Topics; if (!string.IsNullOrEmpty(usrDir)) outputDir = usrDir; @@ -155,33 +177,42 @@ void ExportSchedule(List storeSchedule) if (!string.IsNullOrEmpty(usrFileName)) outputFile = usrFileName; - if (usrList.Length > 0) - defaultTopics = usrList; + // If array is populated, apply config + if (!usrTopics.Any()) + { + foreach (var usrTopic in usrTopics) + topics.Add(usrTopic); + } // Set new file Path - filePath = Path.Combine(outputDir, outputFile); + filePath = Path.Combine(outputDir, outputFile!); } - topic = NewTopic(defaultTopics.ToList()); + // Set new topic + topics = config.Topics.ToList(); + chosenTopic = NewTopic(topics); - // If the file already exists, assume a previous schedule was written - if (File.Exists(filePath)) + // Write to file. + JsonWriterOptions options = new() { - if (UserChoice("Add to existing file?")) - appendSchedule = true; + Indented = true + }; - // Write to file. - using (var outputFile = new StreamWriter(filePath, appendSchedule)) - { - outputFile.WriteLine($" === {topic} ==="); - foreach (var line in storeSchedule) - outputFile.WriteLine(line); - } - } + var jsonList = JsonSerializer.Deserialize>(filePath) + ?? new List(); - // Clear list from memory before exit + jsonList.Add(new Schedule() + { + Topic = chosenTopic, + Times = storeSchedule, + }); + var jsonString = JsonSerializer.Serialize(jsonList); + Console.WriteLine(jsonString); + // File.WriteAllText(filePath, jsonString); + Console.WriteLine($"{Environment.NewLine}Written to: {filePath}"); + + // Clear list from memory storeSchedule.Clear(); - } /// @@ -193,13 +224,14 @@ void PrintSchedule(bool isRestart = false) var storeSchedule = new List(); var scheduledTimes = GenerateSchedule(); + // Clear the screen on restart if (isRestart) Console.Clear(); Console.WriteLine("=== Publish Times ==="); foreach (var time in scheduledTimes) { - var articleTime = $"Article {scheduledTimes.IndexOf(time) + 1} Scheduled at: {ConvertTo12Hour(time)}"; + var articleTime = $"{ConvertTo12Hour(time)}"; // Correct format string to display time in 12-hour format with AM/PM Console.WriteLine(articleTime); // Store the schedule to memory for option export diff --git a/README.md b/README.md index 982e6d5..5322e33 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # Publish Times -This is a very simple console application that generates a list of times to publish news articles within a randomized 2-3 hour delay while avoiding time conflicts within a 30-minute window. +This is a very simple console application that generates a list of times to publish news articles within a randomized 2-3 hour delay within a 30-minute window to avoid conflicts. This keeps thing flowing at an organic and slow pace. + +It is not recommended for use with hot topics. Instead, you should focus on overlooked foreign affairs, local news or op-eds. Of course, this is just covering general news. ## Exporting Once it generates a list of times, you can retry or export the times. If you export, you're requested to select from a number of topics or default to none if no selection is made. Upon export, you can start over or exit. - Choice selection is based on the Y/N keys. ``Enter`` works the same as ``Y`` while pressing any other key is the equivalent of ``N``. You can make mistakes, but it expects failure. +Choice selection is based on the Y/N keys. ``Enter`` works the same as ``Y`` while pressing any other key is the equivalent of ``N``. You can make mistakes, but it expects failure. An optional ``config.toml`` file allows for further customization of file name, directory, and topics. @@ -14,6 +16,8 @@ An optional ``config.toml`` file allows for further customization of file name, A while back, I [found a tool](https://schedule.lemmings.world) to schedule articles on Lemmy. I've been posting within a few hours apart at random minutes and I wanted to something decide that for me. I had AI write the base algorithm, everything else is my own touches. +This tool was originally intended with a simple purpose: suggest some publishing times. However, when customable topics were added, it became more versatile. It can be used with anything that can be scheduled to publish, from news, art, or anything else. + ## License I hereby waive this project under the public domain - see [UNLICENSE](UNLICENSE) for details. diff --git a/Schedule.cs b/Schedule.cs new file mode 100644 index 0000000..7b7ed6f --- /dev/null +++ b/Schedule.cs @@ -0,0 +1,9 @@ + +public class Schedule +{ + [JsonPropertyName("topic")] + public string Topic { get; set; } = ""; + + [JsonPropertyName("times")] + public IList Times { get; set; } = new List(); +} diff --git a/config.toml.sample b/config.toml.sample index 6bdc33d..c17da12 100644 --- a/config.toml.sample +++ b/config.toml.sample @@ -1,5 +1,5 @@ path = "/home/tonytins/Documents/" -file = "newscycle.txt" +file = "newscycle.json" topics = [ "Games", "News", From 2dd63ccba073075a54e12db97ac01e070467ad14 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sun, 16 Mar 2025 12:58:46 -0400 Subject: [PATCH 2/3] Fixed JSON error - If JSON file doesn't exist, write one that does and add brackets. - Trim any white space in the values --- Config.cs | 2 +- Program.cs | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Config.cs b/Config.cs index 933c122..b5c236f 100644 --- a/Config.cs +++ b/Config.cs @@ -10,7 +10,7 @@ public class Config /// /// Gets or sets the name of the schedule file. /// - public string File { get; set; } = "schedule.txt"; + public string File { get; set; } = "schedule.json"; /// /// Gets or sets the directory path where the schedule file is stored. diff --git a/Program.cs b/Program.cs index 2fdbc2c..631fc3d 100644 --- a/Program.cs +++ b/Program.cs @@ -146,7 +146,7 @@ string NewTopic(List topics) /// the directory, filename, and list of topics based on /// a configuration file if available. /// -void ExportSchedule(List storeSchedule) +void ExportSchedule(List storeTimes) { // App directory is used for config file var appDir = AppDomain.CurrentDomain.BaseDirectory; @@ -160,6 +160,7 @@ void ExportSchedule(List storeSchedule) var outputFile = config.File; var filePath = Path.Combine(outputDir, outputFile!); var chosenTopic = ""; + var times = new List(); // If the config file exists, read from that but don't assume anything is filled @@ -188,31 +189,35 @@ void ExportSchedule(List storeSchedule) filePath = Path.Combine(outputDir, outputFile!); } + if (!File.Exists(filePath)) + File.WriteAllText(filePath, "[]"); + + foreach (var time in storeTimes) + times.Add(time.Trim()); + // Set new topic topics = config.Topics.ToList(); chosenTopic = NewTopic(topics); // Write to file. - JsonWriterOptions options = new() - { - Indented = true - }; + var jsonContent = File.ReadAllText(filePath); + var jsonList = string.IsNullOrWhiteSpace(jsonContent) ? new List() + : JsonSerializer.Deserialize>(jsonContent) ?? new List(); - var jsonList = JsonSerializer.Deserialize>(filePath) - ?? new List(); jsonList.Add(new Schedule() { - Topic = chosenTopic, - Times = storeSchedule, + Topic = chosenTopic.Trim(), + Times = times, }); + var jsonString = JsonSerializer.Serialize(jsonList); - Console.WriteLine(jsonString); - // File.WriteAllText(filePath, jsonString); + Console.WriteLine(jsonList); + File.WriteAllText(filePath, jsonString); Console.WriteLine($"{Environment.NewLine}Written to: {filePath}"); // Clear list from memory - storeSchedule.Clear(); + storeTimes.Clear(); } /// From 0c18cb0c34b8df9518fb0ef4fcaabea433b2b747 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sun, 16 Mar 2025 16:01:18 -0400 Subject: [PATCH 3/3] Fixed loads of oversights - Forgot I used IList instead of List - Introduced the Tracer class. This allows writing text to the terminal in Debug mode, regardless of if a Debugger is attached. Added version to 0.2.101 (0.1 outputting the original .txt file) - Default config settings (if no file is found) is now set in GetConfig() Honestly, this turned out to be bigger than I anticipated, but I'm enjoying myself. --- Config.cs | 7 ++--- GlobalUsings.cs | 1 + Program.cs | 63 +++++++++++++++++++++++++++---------------- PublishTimes.csproj | 1 + README.md | 19 ++++++++++++- Tracer.cs | 66 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 Tracer.cs diff --git a/Config.cs b/Config.cs index b5c236f..14a4dc2 100644 --- a/Config.cs +++ b/Config.cs @@ -10,15 +10,16 @@ public class Config /// /// Gets or sets the name of the schedule file. /// - public string File { get; set; } = "schedule.json"; + public string? File { get; set; } /// /// Gets or sets the directory path where the schedule file is stored. /// - public string Path { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + public string? Path { get; set; } /// /// Gets or sets the list of available topics from the configuration file. /// - public IList Topics { get; set; } = new[] { "Games", "Politics", "Research", "Technology" }; + public List Topics { get; set; } = new List(); + } diff --git a/GlobalUsings.cs b/GlobalUsings.cs index 6e12d2f..5e35567 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -1,3 +1,4 @@ +global using System.Diagnostics; global using Tomlyn; global using Tomlyn.Model; global using PublishTimes; diff --git a/Program.cs b/Program.cs index 631fc3d..c5dd133 100644 --- a/Program.cs +++ b/Program.cs @@ -18,8 +18,13 @@ Config GetConfig(string file = "config.toml") return model; } - - return new Config(); + var defaultList = new[] { "Games", "Politics", "Research", "Technology" }; + return new Config() + { + File = "schedule.json", + Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), + Topics = defaultList.ToList() + }; } /// @@ -42,7 +47,7 @@ bool UserChoice(string choice) /// delay between each while avoiding time conflicts within a 30-minute window. /// /// A list of TimeSpan objects representing scheduled article times. -List GenerateSchedule() +List GenerateTimes() { var numberOfArticles = 5; // Define how many articles to schedule var startTime = new TimeSpan(9, 0, 0); // Starting time at 9:00 AM @@ -149,7 +154,7 @@ string NewTopic(List topics) void ExportSchedule(List storeTimes) { // App directory is used for config file - var appDir = AppDomain.CurrentDomain.BaseDirectory; + var appDir = Tracer.AppDirectory; // File directory is used for file location set in config var outputDir = Directory.GetCurrentDirectory(); var cfgFile = "config.toml"; @@ -166,24 +171,32 @@ void ExportSchedule(List storeTimes) // If the config file exists, read from that but don't assume anything is filled if (File.Exists(cfgPath)) { + Tracer.WriteLine(cfgPath); var toml = File.ReadAllText(cfgPath); var usrDir = config.Path; var usrFileName = config.File; // Convert list into array - var usrTopics = config.Topics; + var list = config.Topics; + var tomlList = string.Join(", ", list); + var usrTopics = tomlList.Split(','); - if (!string.IsNullOrEmpty(usrDir)) - outputDir = usrDir; + if (string.IsNullOrEmpty(usrDir)) + return; - if (!string.IsNullOrEmpty(usrFileName)) - outputFile = usrFileName; + outputDir = usrDir; + + if (string.IsNullOrEmpty(usrFileName)) + return; + + outputFile = usrFileName; + + // If array is empty, return; otherwise, apply config + if (usrTopics.Length < 0) + return; + + foreach (var usrTopic in usrTopics) + topics.Add(usrTopic); - // If array is populated, apply config - if (!usrTopics.Any()) - { - foreach (var usrTopic in usrTopics) - topics.Add(usrTopic); - } // Set new file Path filePath = Path.Combine(outputDir, outputFile!); @@ -211,10 +224,14 @@ void ExportSchedule(List storeTimes) Times = times, }); - var jsonString = JsonSerializer.Serialize(jsonList); - Console.WriteLine(jsonList); + var jsonOptions = new JsonSerializerOptions() + { + WriteIndented = true, + }; + + var jsonString = JsonSerializer.Serialize(jsonList, jsonOptions); File.WriteAllText(filePath, jsonString); - Console.WriteLine($"{Environment.NewLine}Written to: {filePath}"); + Tracer.WriteLine($"{jsonString}{Environment.NewLine}Written to: {filePath}"); // Clear list from memory storeTimes.Clear(); @@ -224,10 +241,10 @@ void ExportSchedule(List storeTimes) /// Displays the scheduled article times in a formatted manner and provides /// options to export the schedule or restart the scheduling process. /// -void PrintSchedule(bool isRestart = false) +void PrintTimes(bool isRestart = false) { var storeSchedule = new List(); - var scheduledTimes = GenerateSchedule(); + var scheduledTimes = GenerateTimes(); // Clear the screen on restart if (isRestart) @@ -245,14 +262,14 @@ void PrintSchedule(bool isRestart = false) // Give the user an option to export the schedule if (UserChoice("Retry?")) - PrintSchedule(true); + PrintTimes(true); // Give the user an option to export the schedule if (UserChoice("Export?")) ExportSchedule(storeSchedule); if (UserChoice("Generate A New Batch?")) - PrintSchedule(true); + PrintTimes(true); else { Console.Clear(); @@ -261,4 +278,4 @@ void PrintSchedule(bool isRestart = false) } // Start the loop -PrintSchedule(); +PrintTimes(); diff --git a/PublishTimes.csproj b/PublishTimes.csproj index 109581c..ef9295c 100644 --- a/PublishTimes.csproj +++ b/PublishTimes.csproj @@ -4,6 +4,7 @@ Exe net9.0 enable + 0.2.101 enable diff --git a/README.md b/README.md index 5322e33..191612d 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,29 @@ It is not recommended for use with hot topics. Instead, you should focus on over ## Exporting -Once it generates a list of times, you can retry or export the times. If you export, you're requested to select from a number of topics or default to none if no selection is made. Upon export, you can start over or exit. +Once it generates a list of times, you can retry or export it as a JSON format. If you export, you're requested to select from a number of topics or default to none if no selection is made. Upon export, you can start over or exit. Choice selection is based on the Y/N keys. ``Enter`` works the same as ``Y`` while pressing any other key is the equivalent of ``N``. You can make mistakes, but it expects failure. An optional ``config.toml`` file allows for further customization of file name, directory, and topics. +### Example + +```json +[ + { + "topic": "Games", + "times": [ + "11:41 AM", + "2:05 PM", + "5:05 PM", + "8:18 PM", + "11:02 PM" + ] + } +] +``` + ## Background A while back, I [found a tool](https://schedule.lemmings.world) to schedule articles on Lemmy. I've been posting within a few hours apart at random minutes and I wanted to something decide that for me. I had AI write the base algorithm, everything else is my own touches. diff --git a/Tracer.cs b/Tracer.cs new file mode 100644 index 0000000..c9dc29c --- /dev/null +++ b/Tracer.cs @@ -0,0 +1,66 @@ +// I hereby waive this project under the public domain - see UNLICENSE for details. +namespace PublishTimes; + +/// +/// Provides debug-only console output methods. +/// These methods are only executed when the application is compiled in DEBUG mode. +/// +internal static class Tracer +{ + /// + /// Writes a line of text to the console, but only when in DEBUG mode. + /// + /// The text to write to the console. + [Conditional("DEBUG")] + internal static void WriteLine(string content) => + Console.WriteLine(content); + + /// + /// Writes text to the console without a newline, but only when in DEBUG mode. + /// + /// The text to write to the console. + [Conditional("DEBUG")] + internal static void Write(string content) => + Console.Write(content); + + /// + /// Writes multiple lines of text to the console, but only when in DEBUG mode. + /// + /// A collection of text lines to write to the console. + [Conditional("DEBUG")] + internal static void WriteLine(IEnumerable contents) + { + foreach (var content in contents) + { + Console.WriteLine(content); + } + } + + /// + /// Writes multiple text entries to the console without newlines, but only when in DEBUG mode. + /// + /// A collection of text entries to write to the console. + [Conditional("DEBUG")] + internal static void Write(IEnumerable contents) + { + foreach (var content in contents) + { + Console.Write(content); + } + } + + /// + /// Gets the current working directory in DEBUG mode or the application's base directory in release mode. + /// + internal static string AppDirectory + { + get + { +#if DEBUG + return Directory.GetCurrentDirectory(); +#else + return AppDomain.CurrentDomain.BaseDirectory; +#endif + } + } +}