diff --git a/00_Utilities/DotnetUtils/.editorconfig b/00_Utilities/DotnetUtils/.editorconfig new file mode 100644 index 00000000..9e7a90d5 --- /dev/null +++ b/00_Utilities/DotnetUtils/.editorconfig @@ -0,0 +1,168 @@ +root = true + + +[*.{cs,vb}] +indent_size = 4 +indent_style = space +end_of_line = crlf +insert_final_newline = true + +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false + +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion + +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent + +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = end_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_private_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_members_should_be_pascal_case.symbols = non_private_members +dotnet_naming_rule.non_private_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.private_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.private_members_should_be_pascal_case.symbols = private_members +dotnet_naming_rule.private_members_should_be_pascal_case.style = camel_case + + +# Symbols for use with naming rules + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum, delegate +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_private_members.applicable_kinds = property, method, field, event +dotnet_naming_symbols.non_private_members.applicable_accessibilities = public, internal, protected, protected_internal, private_protected + +dotnet_naming_symbols.private_members.applicable_kinds = property, method, field, event +dotnet_naming_symbols.private_members.applicable_accessibilities = private + + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.camel_case.required_prefix = +dotnet_naming_style.camel_case.required_suffix = +dotnet_naming_style.camel_case.word_separator = +dotnet_naming_style.camel_case.capitalization = camel_case + + +[*.cs] +csharp_new_line_before_catch = false +csharp_new_line_before_else = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = none +csharp_new_line_between_query_expression_clauses = true + +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +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_around_declaration_statements = false +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_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +csharp_prefer_braces = true:warning + +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion + +csharp_prefer_simple_default_expression = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion + +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion + +csharp_preferred_modifier_order = internal,protected,public,private,static,readonly,abstract,override,sealed,virtual:suggestion + +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 +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + + +[*.vb] +visual_basic_preferred_modifier_order = partial,default,private,protected,public,friend,notoverridable,overridable,mustoverride,overloads,overrides,mustinherit,notinheritable,static,shared,shadows,readonly,writeonly,dim,const,withevents,widening,narrowing,custom,async,iterator:silent +visual_basic_style_unused_value_assignment_preference = unused_local_variable:suggestion +visual_basic_style_unused_value_expression_statement_preference = unused_local_variable:silent diff --git a/00_Utilities/DotnetUtils/DotnetUtils.sln b/00_Utilities/DotnetUtils/DotnetUtils.sln new file mode 100644 index 00000000..ecf9588c --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetUtils", "DotnetUtils\DotnetUtils.csproj", "{BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {30FCF56E-4E83-42F8-AB43-A52C86C7C9B4} + EndGlobalSection +EndGlobal diff --git a/00_Utilities/DotnetUtils/DotnetUtils/DotnetUtils.csproj b/00_Utilities/DotnetUtils/DotnetUtils/DotnetUtils.csproj new file mode 100644 index 00000000..74abf5c9 --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils/DotnetUtils.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/00_Utilities/DotnetUtils/DotnetUtils/Extensions.cs b/00_Utilities/DotnetUtils/DotnetUtils/Extensions.cs new file mode 100644 index 00000000..82ff9532 --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils/Extensions.cs @@ -0,0 +1,27 @@ +using System.Diagnostics.CodeAnalysis; + +namespace DotnetUtils; + +public static class Extensions { + public static IEnumerable SelectT(this IEnumerable<(T1, T2)> src, Func selector) => + src.Select(x => selector(x.Item1, x.Item2)); + public static IEnumerable SelectT(this IEnumerable<(T1, T2, T3)> src, Func selector) => + src.Select(x => selector(x.Item1, x.Item2, x.Item3)); + public static IEnumerable<(T1, T2, int)> WithIndex(this IEnumerable<(T1, T2)> src) => src.Select((x, index) => (x.Item1, x.Item2, index)); + + public static bool IsNullOrWhitespace([NotNullWhen(false)] this string? s) => string.IsNullOrWhiteSpace(s); + + [return: NotNullIfNotNull("path")] + public static string? RelativePath(this string? path, string? rootPath) { + if ( + path.IsNullOrWhitespace() || + rootPath.IsNullOrWhitespace() + ) { return path; } + + var path1 = path.TrimEnd('\\'); + rootPath = rootPath.TrimEnd('\\'); + if (!path1.StartsWith(rootPath, StringComparison.InvariantCultureIgnoreCase)) { return path; } + + return path1[(rootPath.Length + 1)..]; // ignore the initial / + } +} diff --git a/00_Utilities/DotnetUtils/DotnetUtils/Globals.cs b/00_Utilities/DotnetUtils/DotnetUtils/Globals.cs new file mode 100644 index 00000000..1ff7c09d --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils/Globals.cs @@ -0,0 +1,8 @@ +namespace DotnetUtils; + +public static class Globals { + public static readonly Dictionary LangData = new() { + { "csharp", ("cs", "csproj") }, + { "vbnet", ("vb", "vbproj") } + }; +} diff --git a/00_Utilities/DotnetUtils/DotnetUtils/PortInfo.cs b/00_Utilities/DotnetUtils/DotnetUtils/PortInfo.cs new file mode 100644 index 00000000..894c8834 --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils/PortInfo.cs @@ -0,0 +1,51 @@ +using System.Reflection; +using static System.IO.Directory; +using static System.IO.Path; +using static DotnetUtils.Globals; + +namespace DotnetUtils; + +public record PortInfo( + string FullPath, string FolderName, int Index, string GameName, + string LangPath, string Lang, string Ext, string ProjExt, + string[] CodeFiles, string[] Slns, string[] Projs +) { + + private static readonly EnumerationOptions enumerationOptions = new() { + RecurseSubdirectories = true, + MatchType = MatchType.Simple, + MatchCasing = MatchCasing.CaseInsensitive + }; + + public static PortInfo? Create(string fullPath, string langKeyword) { + var folderName = GetFileName(fullPath); + var parts = folderName.Split('_', 2); + + var index = + parts.Length > 0 && int.TryParse(parts[0], out var n) ? + n : + (int?)null; + + var gameName = + parts.Length > 1 ? + parts[1].Replace("_", "") : + null; + + if (index is 0 or null || gameName is null) { return null; } + + var (ext, projExt) = LangData[langKeyword]; + var langPath = Combine(fullPath, langKeyword); + var codeFiles = + GetFiles(langPath, $"*.{ext}", enumerationOptions) + .Where(x => !x.Contains("\\bin\\") && !x.Contains("\\obj\\")) + .ToArray(); + + return new PortInfo( + fullPath, folderName, index.Value, gameName, + langPath, langKeyword, ext, projExt, + codeFiles, + GetFiles(langPath, "*.sln", enumerationOptions), + GetFiles(langPath, $"*.{projExt}", enumerationOptions) + ); + } +} diff --git a/00_Utilities/DotnetUtils/DotnetUtils/PortInfos.cs b/00_Utilities/DotnetUtils/DotnetUtils/PortInfos.cs new file mode 100644 index 00000000..b8c1afd3 --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils/PortInfos.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using static System.IO.Directory; +using static DotnetUtils.Globals; + +namespace DotnetUtils; + +public static class PortInfos { + public static readonly string Root; + + static PortInfos() { + Root = GetParent(Assembly.GetEntryAssembly()!.Location)!.FullName; + Root = Root[..Root.IndexOf(@"\00_Utilities")]; + + Get = GetDirectories(Root) + .SelectMany(fullPath => LangData.Keys.Select(keyword => (fullPath, keyword))) + .SelectT((fullPath, keyword) => PortInfo.Create(fullPath, keyword)) + .Where(x => x is not null) + .ToArray()!; + } + + public static readonly PortInfo[] Get; +} diff --git a/00_Utilities/DotnetUtils/DotnetUtils/Program.cs b/00_Utilities/DotnetUtils/DotnetUtils/Program.cs new file mode 100644 index 00000000..9bf36f4f --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils/Program.cs @@ -0,0 +1,163 @@ +using DotnetUtils; +using static System.Console; +using static System.IO.Path; + +var infos = PortInfos.Get; + +var actions = new (Action action, string description)[] { + (printInfos, "Output information -- solution, project, and code files"), + (missingSln, "Output missing sln"), + (unexpectedSlnName, "Output misnamed sln"), + (multipleSlns, "Output multiple sln files"), + (missingProj, "Output missing project file"), + (unexpectedProjName, "Output misnamed project files"), + (multipleProjs, "Output multiple project files") +}; + +foreach (var (_, description, index) in actions.WithIndex()) { + WriteLine($"{index}: {description}"); +} + +WriteLine(); + +actions[getChoice(actions.Length - 1)].action(); + +int getChoice(int maxValue) { + int result; + do { + Write("? "); + } while (!int.TryParse(ReadLine(), out result) || result < 0 || result > maxValue); + WriteLine(); + return result; +} + +void printSlns(PortInfo pi) { + switch (pi.Slns.Length) { + case 0: + WriteLine("No sln"); + break; + case 1: + WriteLine($"Solution: {pi.Slns[0].RelativePath(pi.LangPath)}"); + break; + case > 1: + WriteLine("Solutions:"); + foreach (var sln in pi.Slns) { + Write(sln.RelativePath(pi.LangPath)); + WriteLine(); + } + break; + } +} + +void printProjs(PortInfo pi) { + switch (pi.Projs.Length) { + case 0: + WriteLine("No project"); + break; + case 1: + WriteLine($"Project: {pi.Projs[0].RelativePath(pi.LangPath)}"); + break; + case > 1: + WriteLine("Projects:"); + foreach (var proj in pi.Projs) { + Write(proj.RelativePath(pi.LangPath)); + WriteLine(); + } + break; + } + WriteLine(); +} + +void printInfos() { + foreach (var item in infos) { + WriteLine(item.LangPath); + WriteLine(); + + printSlns(item); + WriteLine(); + + printProjs(item); + WriteLine(); + + // get code files + foreach (var file in item.CodeFiles) { + WriteLine(file.RelativePath(item.LangPath)); + } + WriteLine(new string('-', 50)); + } +} + +void missingSln() { + var data = infos.Where(x => !x.Slns.Any()).ToArray(); + foreach (var item in data) { + WriteLine(item.LangPath); + } + WriteLine(); + WriteLine($"Count: {data.Length}"); +} + +void unexpectedSlnName() { + var counter = 0; + foreach (var item in infos) { + if (!item.Slns.Any()) { continue; } + + var expectedSlnName = $"{item.GameName}.sln"; + if (item.Slns.Contains(Combine(item.LangPath, expectedSlnName))) { continue; } + + counter += 1; + WriteLine(item.LangPath); + WriteLine($"Expected: {expectedSlnName}"); + + printSlns(item); + + WriteLine(); + } + WriteLine($"Count: {counter}"); +} + +void multipleSlns() { + var data = infos.Where(x => x.Slns.Length > 1).ToArray(); + foreach (var item in data) { + WriteLine(item.LangPath); + printSlns(item); + } + WriteLine(); + WriteLine($"Count: {data.Length}"); +} + +void missingProj() { + var data = infos.Where(x => !x.Projs.Any()).ToArray(); + foreach (var item in data) { + WriteLine(item.LangPath); + } + WriteLine(); + WriteLine($"Count: {data.Length}"); +} + +void unexpectedProjName() { + var counter = 0; + foreach (var item in infos) { + if (!item.Projs.Any()) { continue; } + + var expectedProjName = $"{item.GameName}.{item.ProjExt}"; + if (item.Projs.Contains(Combine(item.LangPath, expectedProjName))) { continue; } + + counter += 1; + WriteLine(item.LangPath); + WriteLine($"Expected: {expectedProjName}"); + + printProjs(item); + + WriteLine(); + } + WriteLine($"Count: {counter}"); +} + +void multipleProjs() { + var data = infos.Where(x => x.Projs.Length > 1).ToArray(); + foreach (var item in data) { + WriteLine(item.LangPath); + } + WriteLine(); + WriteLine($"Count: {data.Length}"); +}