diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index dd2d063..fbe943e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,22 +3,25 @@ "isRoot": true, "tools": { "cake.tool": { - "version": "2.0.0", + "version": "6.1.0", "commands": [ "dotnet-cake" - ] + ], + "rollForward": false }, "gitversion.tool": { - "version": "5.8.1", + "version": "6.6.2", "commands": [ "dotnet-gitversion" - ] + ], + "rollForward": false }, "gitissue.tool": { "version": "0.1.1-alpha-01", "commands": [ "gitissue" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8db8769..01d6450 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: branches: - main pull_request: - branches: + branches: - '**' jobs: @@ -13,17 +13,17 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [windows-latest, ubuntu-18.04] + os: [windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: clean: true fetch-depth: 0 - - name: Setup - uses: actions/setup-dotnet@v1 + - name: Setup .NET + uses: actions/setup-dotnet@v4 with: - dotnet-version: '6.0.x' + dotnet-version: '10.0.x' - name: Build uses: cake-build/cake-action@v1 with: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e160aa7..e1f2e85 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,14 +8,14 @@ jobs: runs-on: windows-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: clean: true fetch-depth: 0 - - name: Setup - uses: actions/setup-dotnet@v1 + - name: Setup .NET + uses: actions/setup-dotnet@v4 with: - dotnet-version: '6.0.x' + dotnet-version: '10.0.x' - name: Push uses: cake-build/cake-action@v1 with: diff --git a/GitVersion.yaml b/GitVersion.yaml new file mode 100644 index 0000000..71fc986 --- /dev/null +++ b/GitVersion.yaml @@ -0,0 +1,5 @@ +workflow: GitFlow/v1 +next-version: 0.2.0 +branches: + main: + label: beta diff --git a/README.md b/README.md index 37e6dbe..67f950d 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ git issue help # Compatibility -This project runs on .NET 5 and is compatible with both Windows and Linux. +This project runs on .NET 10 and is compatible with both Windows and Linux. ## Configuration diff --git a/build.cake b/build.cake index d54940a..48ab7cc 100644 --- a/build.cake +++ b/build.cake @@ -35,8 +35,8 @@ Task("Publish") { DotNetPublish("src/GitIssue.Tool/GitIssue.Tool.csproj", new DotNetPublishSettings { - Framework = "net6.0", - Runtime = "win10-x64", + Framework = "net10.0", + Runtime = "win-x64", PublishReadyToRun = false, PublishTrimmed = false, SelfContained = true, diff --git a/src/.editorconfig b/src/.editorconfig index 4b3a5d7..dccd6ed 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -1,5 +1,5 @@ -# Remove the line below if you want to inherit .editorconfig settings from higher directories root = true +# Remove the line below if you want to inherit .editorconfig settings from higher directories # C# files [*.cs] @@ -12,7 +12,6 @@ indent_style = space tab_width = 4 # New line preferences -end_of_line = crlf insert_final_newline = false #### .NET Coding Conventions #### @@ -20,26 +19,26 @@ insert_final_newline = false # Organize usings dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = true -file_header_template = unset +file_header_template = # this. and Me. preferences -dotnet_style_qualification_for_event = false -dotnet_style_qualification_for_field = false -dotnet_style_qualification_for_method = false -dotnet_style_qualification_for_property = false +dotnet_style_qualification_for_event = true:warning +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_property = true:warning # Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true -dotnet_style_predefined_type_for_member_access = true +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion # Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion dotnet_style_parentheses_in_other_operators = never_if_unnecessary -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion # Expression-level preferences dotnet_style_coalesce_expression = true @@ -70,9 +69,9 @@ dotnet_remove_unnecessary_suppression_exclusions = none #### C# Coding Conventions #### # var preferences -csharp_style_var_elsewhere = false -csharp_style_var_for_built_in_types = false -csharp_style_var_when_type_is_apparent = true +csharp_style_var_elsewhere = false:suggestion +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:suggestion # Expression-bodied members csharp_style_expression_bodied_accessors = true @@ -96,10 +95,9 @@ csharp_style_conditional_delegate_call = true # Modifier preferences csharp_prefer_static_local_function = true -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion # Code-block preferences -csharp_prefer_braces = true csharp_prefer_simple_using_statement = true # Expression-level preferences @@ -115,7 +113,6 @@ csharp_style_unused_value_assignment_preference = discard_variable csharp_style_unused_value_expression_statement_preference = discard_variable # 'using' directive preferences -csharp_using_directive_placement = outside_namespace #### C# Formatting Rules #### @@ -124,7 +121,7 @@ csharp_new_line_before_catch = true csharp_new_line_before_else = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_object_initializers = false csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true @@ -205,3 +202,322 @@ 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 + +# Microsoft .NET properties +csharp_style_namespace_declarations = block_scoped:warning +csharp_style_prefer_utf8_string_literals = true:suggestion +dotnet_naming_rule.private_constants_rule.import_to_resharper = True +dotnet_naming_rule.private_constants_rule.resharper_description = Constant fields (private) +dotnet_naming_rule.private_constants_rule.resharper_guid = 236f7aa5-7b06-43ca-bf2a-9b31bfcff09a +dotnet_naming_rule.private_constants_rule.severity = warning +dotnet_naming_rule.private_constants_rule.style = lower_camel_case_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = True +dotnet_naming_rule.private_instance_fields_rule.resharper_description = Instance fields (private) +dotnet_naming_rule.private_instance_fields_rule.resharper_guid = 4a98fdf6-7d98-4f5a-afeb-ea44ad98c70c +dotnet_naming_rule.private_instance_fields_rule.severity = warning +dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_methods_override_rule.import_to_resharper = False +dotnet_naming_rule.private_methods_override_rule.severity = warning +dotnet_naming_rule.private_methods_override_rule.style = pascal_case +dotnet_naming_rule.private_methods_override_rule.symbols = private_methods_override_symbols +dotnet_naming_rule.private_methods_rule.import_to_resharper = True +dotnet_naming_rule.private_methods_rule.resharper_description = Private methods +dotnet_naming_rule.private_methods_rule.resharper_guid = 9fd73c3c-2baa-4abe-b19a-924d8ec71ca4 +dotnet_naming_rule.private_methods_rule.severity = warning +dotnet_naming_rule.private_methods_rule.style = lower_camel_case_style +dotnet_naming_rule.private_methods_rule.symbols = private_methods_symbols +dotnet_naming_rule.private_static_fields_rule.import_to_resharper = True +dotnet_naming_rule.private_static_fields_rule.resharper_description = Static fields (private) +dotnet_naming_rule.private_static_fields_rule.resharper_guid = f9fce829-e6f4-4cb2-80f1-5497c44f51df +dotnet_naming_rule.private_static_fields_rule.severity = warning +dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = True +dotnet_naming_rule.private_static_readonly_rule.resharper_description = Static readonly fields (private) +dotnet_naming_rule.private_static_readonly_rule.resharper_guid = 15b5b1f1-457c-4ca6-b278-5615aedc07d3 +dotnet_naming_rule.private_static_readonly_rule.severity = warning +dotnet_naming_rule.private_static_readonly_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_rule.private_static_rule.import_to_resharper = True +dotnet_naming_rule.private_static_rule.resharper_description = Private static +dotnet_naming_rule.private_static_rule.resharper_guid = 1fa21c09-c5d8-42d5-82ba-9f245a59f967 +dotnet_naming_rule.private_static_rule.severity = warning +dotnet_naming_rule.private_static_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_rule.symbols = private_static_symbols +dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True +dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field +dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef +dotnet_naming_rule.unity_serialized_field_rule.severity = warning +dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style +dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_constants_symbols.resharper_applicable_kinds = constant_field +dotnet_naming_symbols.private_constants_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_instance_fields_symbols.resharper_applicable_kinds = field,readonly_field +dotnet_naming_symbols.private_instance_fields_symbols.resharper_required_modifiers = instance +dotnet_naming_symbols.private_methods_override_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_methods_override_symbols.applicable_kinds = class +dotnet_naming_symbols.private_methods_override_symbols.required_modifiers = static +dotnet_naming_symbols.private_methods_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_methods_symbols.applicable_kinds = class,field,method +dotnet_naming_symbols.private_methods_symbols.resharper_applicable_kinds = async_method,field,readonly_field,class,method +dotnet_naming_symbols.private_methods_symbols.resharper_required_modifiers = instance +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_fields_symbols.resharper_applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = readonly,static +dotnet_naming_symbols.private_static_readonly_symbols.resharper_applicable_kinds = readonly_field +dotnet_naming_symbols.private_static_readonly_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.private_static_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_symbols.applicable_kinds = method +dotnet_naming_symbols.private_static_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_symbols.resharper_applicable_kinds = method +dotnet_naming_symbols.private_static_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance + +# ReSharper properties +resharper_align_multiline_binary_expressions_chain = false +resharper_align_multiple_declaration = true +resharper_braces_for_for = required +resharper_braces_for_foreach = required +resharper_braces_for_ifelse = required +resharper_braces_for_while = required +resharper_cpp_insert_final_newline = true +resharper_csharp_brace_style = next_line +resharper_csharp_keep_blank_lines_in_code = 3 +resharper_csharp_keep_blank_lines_in_declarations = 3 +resharper_csharp_keep_existing_enum_arrangement = false +resharper_csharp_max_line_length = 320 +resharper_csharp_use_indent_from_vs = false +resharper_csharp_wrap_before_first_type_parameter_constraint = true +resharper_default_value_when_type_not_evident = default_expression +resharper_keep_existing_declaration_block_arrangement = false +resharper_new_line_before_while = true +resharper_object_creation_when_type_evident = explicitly_typed +resharper_parentheses_non_obvious_operations = none, shift, relational, equality, bitwise_and, bitwise_exclusive_or, bitwise_inclusive_or, bitwise, conditional_and, conditional_or, conditional +resharper_place_accessorholder_attribute_on_same_line = false +resharper_place_field_attribute_on_same_line = if_owner_is_single_line +resharper_place_simple_embedded_statement_on_same_line = false +resharper_static_members_qualify_members = none, field, property, event, method +resharper_trailing_comma_in_multiline_lists = true +resharper_use_heuristics_for_body_style = true +resharper_wrap_chained_binary_expressions = chop_if_long +resharper_xmldoc_attribute_indent = align_by_first_attribute +resharper_xmldoc_linebreaks_inside_tags_for_elements_longer_than = 80 +resharper_xmldoc_max_blank_lines_between_tags = 1 +resharper_xmldoc_max_line_length = 160 +resharper_xml_attribute_indent = single_indent +resharper_xml_attribute_style = on_different_lines +resharper_xml_max_line_length = 320 +resharper_xml_pi_attributes_indent = single_indent +resharper_xml_use_indent_from_vs = false + +# ReSharper inspection severities +resharper_arrange_attributes_highlighting = suggestion +resharper_arrange_default_value_when_type_not_evident_highlighting = suggestion +resharper_arrange_method_or_operator_body_highlighting = suggestion +resharper_arrange_object_creation_when_type_not_evident_highlighting = suggestion +resharper_arrange_static_member_qualifier_highlighting = warning +resharper_arrange_trailing_comma_in_multiline_lists_highlighting = suggestion +resharper_arrange_trailing_comma_in_singleline_lists_highlighting = suggestion +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_cpp_enforce_nested_namespaces_style_highlighting = suggestion +resharper_enforce_do_while_statement_braces_highlighting = suggestion +resharper_enforce_fixed_statement_braces_highlighting = suggestion +resharper_enforce_foreach_statement_braces_highlighting = suggestion +resharper_enforce_for_statement_braces_highlighting = suggestion +resharper_enforce_if_statement_braces_highlighting = suggestion +resharper_enforce_lock_statement_braces_highlighting = suggestion +resharper_enforce_using_statement_braces_highlighting = suggestion +resharper_enforce_while_statement_braces_highlighting = suggestion +resharper_redundant_bool_compare_highlighting = none +resharper_suggest_discard_declaration_var_style_highlighting = suggestion +resharper_suggest_var_or_type_elsewhere_highlighting = warning + +[*.cql] +indent_style = tab +indent_size = tab +tab_width = 4 + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers = false +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion +csharp_style_namespace_declarations = block_scoped:warning +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_var_elsewhere = false:suggestion +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:suggestion +dotnet_naming_rule.private_constants_rule.import_to_resharper = True +dotnet_naming_rule.private_constants_rule.resharper_description = Constant fields (private) +dotnet_naming_rule.private_constants_rule.resharper_guid = 236f7aa5-7b06-43ca-bf2a-9b31bfcff09a +dotnet_naming_rule.private_constants_rule.severity = warning +dotnet_naming_rule.private_constants_rule.style = lower_camel_case_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = True +dotnet_naming_rule.private_instance_fields_rule.resharper_description = Instance fields (private) +dotnet_naming_rule.private_instance_fields_rule.resharper_guid = 4a98fdf6-7d98-4f5a-afeb-ea44ad98c70c +dotnet_naming_rule.private_instance_fields_rule.severity = warning +dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_methods_override_rule.import_to_resharper = False +dotnet_naming_rule.private_methods_override_rule.severity = warning +dotnet_naming_rule.private_methods_override_rule.style = upper_camel_case_style +dotnet_naming_rule.private_methods_override_rule.symbols = private_methods_override_symbols +dotnet_naming_rule.private_methods_rule.import_to_resharper = True +dotnet_naming_rule.private_methods_rule.resharper_description = Private methods +dotnet_naming_rule.private_methods_rule.resharper_guid = 9fd73c3c-2baa-4abe-b19a-924d8ec71ca4 +dotnet_naming_rule.private_methods_rule.severity = warning +dotnet_naming_rule.private_methods_rule.style = lower_camel_case_style +dotnet_naming_rule.private_methods_rule.symbols = private_methods_symbols +dotnet_naming_rule.private_static_fields_rule.import_to_resharper = True +dotnet_naming_rule.private_static_fields_rule.resharper_description = Static fields (private) +dotnet_naming_rule.private_static_fields_rule.resharper_guid = f9fce829-e6f4-4cb2-80f1-5497c44f51df +dotnet_naming_rule.private_static_fields_rule.severity = warning +dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = True +dotnet_naming_rule.private_static_readonly_rule.resharper_description = Static readonly fields (private) +dotnet_naming_rule.private_static_readonly_rule.resharper_guid = 15b5b1f1-457c-4ca6-b278-5615aedc07d3 +dotnet_naming_rule.private_static_readonly_rule.severity = warning +dotnet_naming_rule.private_static_readonly_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_rule.private_static_rule.import_to_resharper = True +dotnet_naming_rule.private_static_rule.resharper_description = Private static +dotnet_naming_rule.private_static_rule.resharper_guid = 1fa21c09-c5d8-42d5-82ba-9f245a59f967 +dotnet_naming_rule.private_static_rule.severity = warning +dotnet_naming_rule.private_static_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_rule.symbols = private_static_symbols +dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True +dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field +dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef +dotnet_naming_rule.unity_serialized_field_rule.severity = warning +dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style +dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_constants_symbols.resharper_applicable_kinds = constant_field +dotnet_naming_symbols.private_constants_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_instance_fields_symbols.resharper_applicable_kinds = field,readonly_field +dotnet_naming_symbols.private_instance_fields_symbols.resharper_required_modifiers = instance +dotnet_naming_symbols.private_methods_override_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_methods_override_symbols.applicable_kinds = class +dotnet_naming_symbols.private_methods_override_symbols.required_modifiers = static +dotnet_naming_symbols.private_methods_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_methods_symbols.applicable_kinds = class,field,method +dotnet_naming_symbols.private_methods_symbols.resharper_applicable_kinds = async_method,field,readonly_field,class,method +dotnet_naming_symbols.private_methods_symbols.resharper_required_modifiers = instance +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_fields_symbols.resharper_applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = readonly,static +dotnet_naming_symbols.private_static_readonly_symbols.resharper_applicable_kinds = readonly_field +dotnet_naming_symbols.private_static_readonly_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.private_static_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_symbols.applicable_kinds = method +dotnet_naming_symbols.private_static_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_symbols.resharper_applicable_kinds = method +dotnet_naming_symbols.private_static_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_qualification_for_event = true:warning +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_property = true:warning +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +# ReSharper properties +resharper_align_multiline_binary_expressions_chain = false +resharper_align_multiple_declaration = true +resharper_braces_for_for = required +resharper_braces_for_foreach = required +resharper_braces_for_ifelse = required +resharper_braces_for_while = required +resharper_csharp_keep_blank_lines_in_code = 3 +resharper_csharp_keep_blank_lines_in_declarations = 3 +resharper_csharp_max_line_length = 320 +resharper_csharp_use_indent_from_vs = false +resharper_csharp_wrap_before_first_type_parameter_constraint = true +resharper_default_value_when_type_not_evident = default_expression +resharper_keep_existing_embedded_block_arrangement = true +resharper_new_line_before_while = true +resharper_object_creation_when_type_evident = explicitly_typed +resharper_parentheses_non_obvious_operations = none, shift, relational, equality, bitwise_and, bitwise_exclusive_or, bitwise_inclusive_or, bitwise, conditional_and, conditional_or, conditional +resharper_place_accessorholder_attribute_on_same_line = false +resharper_place_field_attribute_on_same_line = if_owner_is_single_line +resharper_place_simple_embedded_block_on_same_line = true +resharper_place_simple_embedded_statement_on_same_line = false +resharper_static_members_qualify_members = none, field, property, event, method +resharper_trailing_comma_in_multiline_lists = true +resharper_wrap_chained_binary_expressions = chop_if_long +resharper_xmldoc_attribute_indent = align_by_first_attribute +resharper_xmldoc_linebreaks_inside_tags_for_elements_longer_than = 80 +resharper_xmldoc_max_blank_lines_between_tags = 1 +resharper_xmldoc_max_line_length = 160 +resharper_xml_attribute_indent = single_indent +resharper_xml_attribute_style = on_different_lines +resharper_xml_max_line_length = 320 +resharper_xml_pi_attributes_indent = single_indent +resharper_xml_use_indent_from_vs = false + +# ReSharper inspection severities +resharper_arrange_attributes_highlighting = suggestion +resharper_arrange_default_value_when_type_not_evident_highlighting = suggestion +resharper_arrange_method_or_operator_body_highlighting = suggestion +resharper_arrange_object_creation_when_type_not_evident_highlighting = suggestion +resharper_arrange_static_member_qualifier_highlighting = warning +resharper_arrange_trailing_comma_in_multiline_lists_highlighting = suggestion +resharper_arrange_trailing_comma_in_singleline_lists_highlighting = suggestion +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_cpp_enforce_nested_namespaces_style_highlighting = suggestion +resharper_enforce_do_while_statement_braces_highlighting = suggestion +resharper_enforce_fixed_statement_braces_highlighting = suggestion +resharper_enforce_foreach_statement_braces_highlighting = suggestion +resharper_enforce_for_statement_braces_highlighting = suggestion +resharper_enforce_if_statement_braces_highlighting = suggestion +resharper_enforce_lock_statement_braces_highlighting = suggestion +resharper_enforce_using_statement_braces_highlighting = suggestion +resharper_enforce_while_statement_braces_highlighting = suggestion +resharper_redundant_bool_compare_highlighting = none +resharper_suggest_discard_declaration_var_style_highlighting = suggestion +resharper_suggest_var_or_type_elsewhere_highlighting = warning + +[*.{appxmanifest,asax,ascx,aspx,axaml,axml,blockshader,c,c++,c++m,cc,ccm,cginc,compute,config,cp,cpp,cppm,cs,cshtml,csproj,cu,cuh,cxx,cxxm,dbml,discomap,dtd,fx,fxh,h,h++,hh,hlsl,hlsli,hlslinc,hp,hpp,htm,html,hxx,icc,inc,inl,ino,ipp,ixx,jsproj,lsproj,master,mpp,mq4,mq5,mqh,mxx,njsproj,nuspec,paml,proj,props,pssl,pssli,razor,resw,resx,shader,shaderFoundry,skin,sql,StyleCop,targets,tasks,tcc,tpp,urtshader,usf,ush,uxml,vb,vbproj,xaml,xamlx,xml,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a80579e..6f87e54 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,43 +1,59 @@ - - $(MSBuildThisFileDirectory) - $(MSBuildThisFileDirectory).output - $(SolutionOutputPath)\$(MSBuildProjectName) - $(ProjectOutputPath)\bin - $(ProjectOutputPath)\obj - $(SolutionOutputPath)\Packages - + + $(MSBuildThisFileDirectory) + $(MSBuildThisFileDirectory).output + $(SolutionOutputPath)\$(MSBuildProjectName) + $(ProjectOutputPath)\bin + $(ProjectOutputPath)\obj + $(SolutionOutputPath)\Packages + - - $(MSBuildBinPath)\Microsoft.CSharp.targets - false - 1701;1702;1705;NU1603;NU5105 - $(ProjectOutputPath)\$(MSBuildProjectName).xml - $(MSBuildProjectName) - + + $(MSBuildBinPath)\Microsoft.CSharp.targets + false + 1701;1702;1705;NU1603;NU5105 + $(ProjectOutputPath)\$(MSBuildProjectName).xml + $(MSBuildProjectName) + - - An 'in-source' issue management system built on GIT. - https://github.com/lennoncork/GitIssue - MIT - README.md - GIT - MIT - + + An 'in-source' issue management system built on GIT. + https://github.com/lennoncork/GitIssue + MIT + README.md + GIT + MIT + - - - - + + + + - - $(DirectoryBuildPath) - $(BaseOutputPath)\$(Configuration) - + + + $(DirectoryBuildPath) + + + $(BaseOutputPath)\$(Configuration) + + - - - + + + \ No newline at end of file diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index d5edd1c..4ab2bfd 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,7 +1,9 @@ - - - + + + \ No newline at end of file diff --git a/src/GitIssue.Tests/Extensions.cs b/src/GitIssue.Tests/Extensions.cs index f8e84be..643e948 100644 --- a/src/GitIssue.Tests/Extensions.cs +++ b/src/GitIssue.Tests/Extensions.cs @@ -6,20 +6,21 @@ namespace GitIssue.Tests public static class Extensions { /// - /// Asserts if the result wasn't successful, otherwise returns the result + /// Asserts if the result wasn't successful, otherwise returns the result /// /// /// /// public static async Task AssertIfNotSuccess(this Task> result) { - var safe = await result; - if (safe.IsSuccess == false && safe.Exception != null) + SafeResult safe = await result; + if (!safe.IsSuccess && (safe.Exception != null)) { TestContext.Out.Write(safe.Exception); } - Assert.That(safe.IsSuccess, Is.True, $"SafeResult was not successful, {safe.Exception?.Message}", safe); + + Assert.That(safe.IsSuccess, Is.True, $"SafeResult was not successful, {safe.Exception?.Message}"); return safe.Result; } } -} +} \ No newline at end of file diff --git a/src/GitIssue.Tests/Formatting/SimpleFormatterTests.cs b/src/GitIssue.Tests/Formatting/SimpleFormatterTests.cs index 8cf7c22..f0df0d3 100644 --- a/src/GitIssue.Tests/Formatting/SimpleFormatterTests.cs +++ b/src/GitIssue.Tests/Formatting/SimpleFormatterTests.cs @@ -5,6 +5,5 @@ namespace GitIssue.Tests.Formatting [TestFixture] public partial class SimpleFormatterTests : TestsBase { - } } \ No newline at end of file diff --git a/src/GitIssue.Tests/Formatting/SimpleFormatterTests_Format.cs b/src/GitIssue.Tests/Formatting/SimpleFormatterTests_Format.cs index 3215965..78e3172 100644 --- a/src/GitIssue.Tests/Formatting/SimpleFormatterTests_Format.cs +++ b/src/GitIssue.Tests/Formatting/SimpleFormatterTests_Format.cs @@ -1,4 +1,5 @@ using GitIssue.Formatters; +using GitIssue.Issues; using NUnit.Framework; namespace GitIssue.Tests.Formatting @@ -12,9 +13,9 @@ public class Format : SimpleFormatterTests [Test] public void FormatsIssue() { - var issue = Moqs.CreateIssue(nameof(FormatsIssue)); - var output = issue.Format(); - var expected = $"{issue.Key}: {issue.Title}"; + IReadOnlyIssue issue = Moqs.CreateIssue(nameof(Format.FormatsIssue)); + string output = issue.Format(); + string expected = $"{issue.Key}: {issue.Title}"; Assert.That(output, Is.EqualTo(expected)); } } diff --git a/src/GitIssue.Tests/GitIssue.Tests.csproj b/src/GitIssue.Tests/GitIssue.Tests.csproj index 4679422..b847f95 100644 --- a/src/GitIssue.Tests/GitIssue.Tests.csproj +++ b/src/GitIssue.Tests/GitIssue.Tests.csproj @@ -1,21 +1,21 @@  - net6.0 + net10.0 enable latest - + - - - - - - + + + + + + diff --git a/src/GitIssue.Tests/Helpers.cs b/src/GitIssue.Tests/Helpers.cs index 1ca882e..218ffab 100644 --- a/src/GitIssue.Tests/Helpers.cs +++ b/src/GitIssue.Tests/Helpers.cs @@ -12,43 +12,24 @@ public static class Helpers public static string TestData = "TestData"; /// - /// Gets a random unique string, max length 64 - /// - /// - /// - public static string GetRandomString(int length = 8) - { - const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[random.Next(s.Length)]).ToArray()); - } - - /// - /// Gets the test directory - /// - /// - public static string GetTestDirectory() - { - return Path.Combine(TestContext.CurrentContext.TestDirectory, TestData); - } - - /// - /// Gets the path of a new temp file, using the default temp directory + /// Creates a new temporary directory, using the default temp directory /// /// - public static string GetTempFile() + public static string CreateTempDirectory() { - return GetTempFile(GetTestDirectory()); + return Helpers.CreateTempDirectory(Helpers.GetTestDirectory()); } /// - /// Gets the path of a new temp file + /// Creates a new temporary directory /// - /// the parent path for the file + /// the parent path for the directory /// - public static string GetTempFile(string path) + public static string CreateTempDirectory(string path) { - return Path.Combine(path, $"{GetRandomString()}.txt"); + string directory = Helpers.GetTempDirectory(path); + Directory.CreateDirectory(directory); + return directory; } /// @@ -57,7 +38,7 @@ public static string GetTempFile(string path) /// public static string CreateTempFile() { - return CreateTempFile(GetTestDirectory()); + return Helpers.CreateTempFile(Helpers.GetTestDirectory()); } /// @@ -67,18 +48,30 @@ public static string CreateTempFile() /// public static string CreateTempFile(string path) { - var file = GetTempFile(path); + string file = Helpers.GetTempFile(path); File.Create(file).Dispose(); return file; } + /// + /// Gets a random unique string, max length 64 + /// + /// + /// + public static string GetRandomString(int length = 8) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[Helpers.random.Next(s.Length)]).ToArray()); + } + /// /// Gets the path of a new temp directory, using the default temp directory /// /// public static string GetTempDirectory() { - return GetTempDirectory(GetTestDirectory()); + return Helpers.GetTempDirectory(Helpers.GetTestDirectory()); } /// @@ -88,28 +81,35 @@ public static string GetTempDirectory() /// public static string GetTempDirectory(string path) { - return Path.Combine(path, GetRandomString()); + return Path.Combine(path, Helpers.GetRandomString()); } /// - /// Creates a new temporary directory, using the default temp directory + /// Gets the path of a new temp file, using the default temp directory /// /// - public static string CreateTempDirectory() + public static string GetTempFile() { - return CreateTempDirectory(GetTestDirectory()); + return Helpers.GetTempFile(Helpers.GetTestDirectory()); } /// - /// Creates a new temporary directory + /// Gets the path of a new temp file /// - /// the parent path for the directory + /// the parent path for the file /// - public static string CreateTempDirectory(string path) + public static string GetTempFile(string path) { - var directory = GetTempDirectory(path); - Directory.CreateDirectory(directory); - return directory; + return Path.Combine(path, $"{Helpers.GetRandomString()}.txt"); + } + + /// + /// Gets the test directory + /// + /// + public static string GetTestDirectory() + { + return Path.Combine(TestContext.CurrentContext.TestDirectory, Helpers.TestData); } /// @@ -127,7 +127,7 @@ public EnvironmentCurrentDirectory(string directory) { if (Directory.Exists(directory)) { - environment = Environment.CurrentDirectory; + this.environment = Environment.CurrentDirectory; Environment.CurrentDirectory = directory; } } @@ -135,8 +135,10 @@ public EnvironmentCurrentDirectory(string directory) /// public void Dispose() { - if (environment != null) - Environment.CurrentDirectory = environment; + if (this.environment != null) + { + Environment.CurrentDirectory = this.environment; + } } } } diff --git a/src/GitIssue.Tests/IntegrationTests/Bug/BugConfiguration.cs b/src/GitIssue.Tests/IntegrationTests/Bug/BugConfiguration.cs index 4978643..c80af3f 100644 --- a/src/GitIssue.Tests/IntegrationTests/Bug/BugConfiguration.cs +++ b/src/GitIssue.Tests/IntegrationTests/Bug/BugConfiguration.cs @@ -14,12 +14,9 @@ public class BugConfiguration : IssueConfiguration /// public BugConfiguration() { - Fields.Add(FieldKey.Create("AffectsVersion"), new FieldInfo(false)); - Fields.Add(FieldKey.Create("FixVersion"), new FieldInfo(false)); - Fields.Add(FieldKey.Create("Severity"), new FieldInfo(false) - { - ValueMetadata = "[S1, S1, S3, S4, S5]" - }); + this.Fields.Add(FieldKey.Create("AffectsVersion"), new FieldInfo(false)); + this.Fields.Add(FieldKey.Create("FixVersion"), new FieldInfo(false)); + this.Fields.Add(FieldKey.Create("Severity"), new FieldInfo(false) { ValueMetadata = "[S1, S1, S3, S4, S5]" }); } } } \ No newline at end of file diff --git a/src/GitIssue.Tests/IntegrationTests/Bug/BugExtensions.cs b/src/GitIssue.Tests/IntegrationTests/Bug/BugExtensions.cs index 226ac96..7a7d3ed 100644 --- a/src/GitIssue.Tests/IntegrationTests/Bug/BugExtensions.cs +++ b/src/GitIssue.Tests/IntegrationTests/Bug/BugExtensions.cs @@ -13,44 +13,44 @@ public static class BugExtensions private static readonly FieldKey SeverityKey = FieldKey.Create("Severity"); /// - /// Sets the fix version of the issue + /// Gets the fix version of the issue /// /// the issue - /// the fix version to set - public static void SetFixVersion(this IIssue issue, Version[] fixVersion) + public static Version[] GetFixVersion(this IIssue issue) { - issue.SetField(FixVersionKey).WithArray(fixVersion); + Version[] fixVersion = issue.GetField(BugExtensions.FixVersionKey).AsArray(); + return fixVersion; } /// - /// Gets the fix version of the issue + /// Gets the severity of the issue /// /// the issue - public static Version[] GetFixVersion(this IIssue issue) + /// + public static Enumerated GetSeverity(this IIssue issue) { - var fixVersion = issue.GetField(FixVersionKey).AsArray(); - return fixVersion; + Enumerated severity = issue.GetField(BugExtensions.SeverityKey).AsValue(); + return severity; } /// - /// Sets the severity of the issue + /// Sets the fix version of the issue /// /// the issue - /// the issue's severity - public static void SetSeverity(this IIssue issue, Enumerated severity) + /// the fix version to set + public static void SetFixVersion(this IIssue issue, Version[] fixVersion) { - issue.SetField(SeverityKey).WithValue(severity); + issue.SetField(BugExtensions.FixVersionKey).WithArray(fixVersion); } /// - /// Gets the severity of the issue + /// Sets the severity of the issue /// /// the issue - /// - public static Enumerated GetSeverity(this IIssue issue) + /// the issue's severity + public static void SetSeverity(this IIssue issue, Enumerated severity) { - var severity = issue.GetField(SeverityKey).AsValue(); - return severity; + issue.SetField(BugExtensions.SeverityKey).WithValue(severity); } } } \ No newline at end of file diff --git a/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests.cs b/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests.cs index 88e3e3a..1d71dff 100644 --- a/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests.cs +++ b/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests.cs @@ -10,17 +10,20 @@ public partial class BugIntegrationTests [SetUp] public void Setup() { - TestDirectory = Helpers.GetTempDirectory(); - if (Directory.Exists(TestDirectory) == false) Directory.CreateDirectory(TestDirectory); + this.TestDirectory = Helpers.GetTempDirectory(); + if (!Directory.Exists(this.TestDirectory)) + { + Directory.CreateDirectory(this.TestDirectory); + } } protected string TestDirectory = Path.GetTempPath(); - protected string GitDirectory => Path.Combine(TestDirectory, Paths.GitFolderName); + protected string GitDirectory => Path.Combine(this.TestDirectory, Paths.GitFolderName); - protected string IssueDirectory => Path.Combine(TestDirectory, Paths.IssueRootFolderName); + protected string IssueDirectory => Path.Combine(this.TestDirectory, Paths.IssueRootFolderName); - protected string ConfigFile => Path.Combine(IssueDirectory, Paths.ConfigFileName); + protected string ConfigFile => Path.Combine(this.IssueDirectory, Paths.ConfigFileName); protected IRepository GitRepository { get; set; } = null!; @@ -29,9 +32,13 @@ public void Setup() [OneTimeSetUp] public void OneTimeSetup() { - TestDirectory = Helpers.GetTestDirectory(); - if (Directory.Exists(TestDirectory)) Directory.Delete(TestDirectory, true); - Directory.CreateDirectory(TestDirectory); + this.TestDirectory = Helpers.GetTestDirectory(); + if (Directory.Exists(this.TestDirectory)) + { + Directory.Delete(this.TestDirectory, true); + } + + Directory.CreateDirectory(this.TestDirectory); } public void Initialize( @@ -40,10 +47,14 @@ public void Initialize( bool initIssue = true) { if (initGit) - GitRepository = new Repository(Repository.Init(directory)); + { + this.GitRepository = new Repository(Repository.Init(directory)); + } if (initIssue) - Issues = GitIssue.IssueManager.Init(new BugConfiguration(), directory); + { + this.Issues = IssueManager.Init(new BugConfiguration(), directory); + } } } } \ No newline at end of file diff --git a/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_FixVersion.cs b/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_FixVersion.cs index dabdf9d..be734f4 100644 --- a/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_FixVersion.cs +++ b/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_FixVersion.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using GitIssue.Issues; using GitIssue.Values; using NUnit.Framework; @@ -14,14 +15,14 @@ public class FixVersion : BugIntegrationTests [Test] public async Task CanBeSetFromString() { - Initialize(TestDirectory); + this.Initialize(this.TestDirectory); - var create = await Issues - .CreateAsync(nameof(CanBeSetFromString), string.Empty) + IIssue create = await this.Issues + .CreateAsync(nameof(FixVersion.CanBeSetFromString), string.Empty) .WithSafeResultAsync() .AssertIfNotSuccess(); - var fixVersion = Version.Parse("1.2.3-abs+def"); + Version fixVersion = Version.Parse("1.2.3-abs+def"); create.SetFixVersion(new[] { fixVersion }); await create @@ -29,9 +30,9 @@ await create .WithSafeResultAsync() .AssertIfNotSuccess(); - var find = Issues.Find(i => i.Key == create.Key).ToArray(); + IIssue[] find = this.Issues.Find(i => i.Key == create.Key).ToArray(); - var issue = find[0]; + IIssue issue = find[0]; Assert.That(issue.GetFixVersion()[0], Is.EqualTo(fixVersion)); } } diff --git a/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_Severity.cs b/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_Severity.cs index e55e63a..1221a63 100644 --- a/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_Severity.cs +++ b/src/GitIssue.Tests/IntegrationTests/Bug/BugIntegrationTests_Severity.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using GitIssue.Issues; using GitIssue.Values; using NUnit.Framework; @@ -14,16 +15,16 @@ public class Severity : BugIntegrationTests [Test] public async Task CanBeSetFromString() { - Initialize(TestDirectory); - var create = await Issues - .CreateAsync(nameof(CanBeSetFromString), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create = await this.Issues + .CreateAsync(nameof(Severity.CanBeSetFromString), string.Empty) .WithSafeResultAsync(); Assert.IsTrue(create.IsSuccess); - var severity = new Enumerated("S1", new[] { "S1", "S2", "S3", "S4", "S5" }); + Enumerated severity = new Enumerated("S1", new[] { "S1", "S2", "S3", "S4", "S5" }); create.Result.SetSeverity(severity); await create.Result.SaveAsync(); - var find = Issues.Find(i => i.Key == create.Result.Key).ToArray(); - var issue = find[0]; + IIssue[] find = this.Issues.Find(i => i.Key == create.Result.Key).ToArray(); + IIssue issue = find[0]; Assert.That(issue.GetSeverity(), Is.EqualTo(severity)); } } diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests.cs index f174121..b1ccc9a 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests.cs @@ -11,8 +11,11 @@ public IIssueManager Sut { get { - if (sut == null) + if (this.sut == null) + { return this.Manager; + } + return this.sut; } set => this.sut = value; diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Create.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Create.cs index ee8aa74..208b29a 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Create.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Create.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using GitIssue.Issues; using NUnit.Framework; namespace GitIssue.Tests.IssueManagerTests @@ -13,8 +14,8 @@ public class Create : IssueManagerTests [TestCase("New Issue", "This Is A New Issue")] public async Task CreatesNewIssue(string title, string description) { - Initialize(TestDirectory); - var create = await Sut + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut .CreateAsync(title, description) .WithSafeResultAsync(); Assert.IsTrue(create.IsSuccess); @@ -25,13 +26,13 @@ public async Task CreatesNewIssue(string title, string description) [Test] public async Task GeneratesUniqueId() { - Initialize(TestDirectory); - var create1 = await Sut - .CreateAsync(nameof(CreatesNewIssue), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create1 = await this.Sut + .CreateAsync(nameof(Create.CreatesNewIssue), string.Empty) .WithSafeResultAsync(); Assert.IsTrue(create1.IsSuccess); - var create2 = await Sut - .CreateAsync(nameof(CreatesNewIssue), string.Empty) + SafeResult create2 = await this.Sut + .CreateAsync(nameof(Create.CreatesNewIssue), string.Empty) .WithSafeResultAsync(); Assert.IsTrue(create2.IsSuccess); Assert.That(create1.Result.Key, Is.Not.EqualTo(create2.Result.Key)); @@ -40,9 +41,9 @@ public async Task GeneratesUniqueId() [Test] public async Task SetsCreatedDate() { - Initialize(TestDirectory); - var create = await Sut - .CreateAsync(nameof(SetsCreatedDate), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut + .CreateAsync(nameof(Create.SetsCreatedDate), string.Empty) .WithSafeResultAsync(); Assert.That((DateTime)create.Result.Created, Is.EqualTo(DateTime.Now).Within(TimeSpan.FromSeconds(2))); } @@ -50,9 +51,9 @@ public async Task SetsCreatedDate() [Test] public async Task SetsUpdatedDate() { - Initialize(TestDirectory); - var create = await Sut - .CreateAsync(nameof(SetsUpdatedDate), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut + .CreateAsync(nameof(Create.SetsUpdatedDate), string.Empty) .WithSafeResultAsync(); Assert.That((DateTime)create.Result.Updated, Is.EqualTo(DateTime.Now).Within(TimeSpan.FromSeconds(2))); } diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Delete.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Delete.cs index a1eb2c6..98bdda9 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Delete.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Delete.cs @@ -1,5 +1,6 @@ using System.IO; using System.Threading.Tasks; +using GitIssue.Issues; using NUnit.Framework; namespace GitIssue.Tests.IssueManagerTests @@ -13,25 +14,25 @@ public class Delete : IssueManagerTests [Test] public async Task DeletesExistingIssue() { - Initialize(TestDirectory); - var create = await Sut - .CreateAsync(nameof(DeletesExistingIssue), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut + .CreateAsync(nameof(Delete.DeletesExistingIssue), string.Empty) .WithSafeResultAsync(); Assert.IsTrue(create.IsSuccess); - Assert.IsTrue(Directory.Exists(Path.Combine(IssueDirectory, Sut.KeyProvider.GetIssuePath(create.Result.Key)))); - var delete = await Sut + Assert.IsTrue(Directory.Exists(Path.Combine(this.IssueDirectory, this.Sut.KeyProvider.GetIssuePath(create.Result.Key)))); + SafeResult delete = await this.Sut .DeleteAsync(create.Result.Key) .WithSafeResultAsync(); Assert.IsTrue(delete.IsSuccess); - Assert.IsFalse(Directory.Exists(Path.Combine(IssueDirectory, Sut.KeyProvider.GetIssuePath(create.Result.Key)))); + Assert.IsFalse(Directory.Exists(Path.Combine(this.IssueDirectory, this.Sut.KeyProvider.GetIssuePath(create.Result.Key)))); } [Test] public async Task FailsIfIssueDoesNotExist() { - Initialize(TestDirectory); - var delete = await Sut - .DeleteAsync(Sut.KeyProvider.Next()) + this.Initialize(this.TestDirectory); + SafeResult delete = await this.Sut + .DeleteAsync(this.Sut.KeyProvider.Next()) .WithSafeResultAsync(); Assert.IsFalse(delete.IsSuccess); } diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Export.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Export.cs index ad33d56..81e0604 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Export.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Export.cs @@ -1,5 +1,6 @@ using System.IO; using System.Threading.Tasks; +using GitIssue.Issues; using GitIssue.Issues.Json; using NUnit.Framework; @@ -14,15 +15,15 @@ public class Export : IssueManagerTests [Test] public async Task ExportsFileToJson() { - Initialize(TestDirectory); - var issue1 = await Sut - .CreateAsync(nameof(ExportsFileToJson)) + this.Initialize(this.TestDirectory); + SafeResult issue1 = await this.Sut + .CreateAsync(nameof(Export.ExportsFileToJson)) .WithSafeResultAsync(); - var issue2 = await Sut - .CreateAsync(nameof(ExportsFileToJson)) + SafeResult issue2 = await this.Sut + .CreateAsync(nameof(Export.ExportsFileToJson)) .WithSafeResultAsync(); - var path = Path.Combine(TestDirectory, "export.json"); - await Sut.ExportAsJsonAsync(path); + string path = Path.Combine(this.TestDirectory, "export.json"); + await this.Sut.ExportAsJsonAsync(path); Assert.That(File.Exists(path), Is.True); } } diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Find.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Find.cs index f99d9fd..58006bd 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Find.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Find.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using GitIssue.Issues; using NUnit.Framework; namespace GitIssue.Tests.IssueManagerTests @@ -13,11 +14,11 @@ public class Find : IssueManagerTests [TestCase("New Issue")] public async Task FindsIssueByTitle(string title) { - Initialize(TestDirectory); - var create = await Sut + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut .CreateAsync(title, string.Empty) .WithSafeResultAsync(); - var find = Sut.Find(i => i.Title == title).ToArray(); + IIssue[] find = this.Sut.Find(i => i.Title == title).ToArray(); Assert.That(find.Count(), Is.EqualTo(1)); Assert.That(find[0].Title, Is.EqualTo(title)); } @@ -25,11 +26,11 @@ public async Task FindsIssueByTitle(string title) [Test] public async Task FindsIssueByKey() { - Initialize(TestDirectory); - var create = await Sut - .CreateAsync(nameof(FindsIssueByKey), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut + .CreateAsync(nameof(Find.FindsIssueByKey), string.Empty) .WithSafeResultAsync(); - var find = Sut.Find(i => i.Key == create.Result.Key).ToArray(); + IIssue[] find = this.Sut.Find(i => i.Key == create.Result.Key).ToArray(); Assert.That(find.Count(), Is.EqualTo(1)); Assert.That(find[0].Key, Is.EqualTo(create.Result.Key)); } diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Init.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Init.cs index cd38049..7e83838 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Init.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Init.cs @@ -14,24 +14,24 @@ public class Init : IssueManagerTests [Test] public void CreatesConfigFile() { - CreatesIssueFolder(); - Assert.IsTrue(File.Exists(ConfigFile)); + this.CreatesIssueFolder(); + Assert.IsTrue(File.Exists(this.ConfigFile)); } [Test] public void CreatesIssueFolder() { - Repository.Init(TestDirectory); - GitIssue.IssueManager.Init(TestDirectory); - Assert.That(TestDirectory, Is.Not.Empty); - Assert.IsTrue(Directory.Exists(IssueDirectory)); - Assert.IsTrue(Directory.Exists(GitDirectory)); + Repository.Init(this.TestDirectory); + IssueManager.Init(this.TestDirectory); + Assert.That(this.TestDirectory, Is.Not.Empty); + Assert.IsTrue(Directory.Exists(this.IssueDirectory)); + Assert.IsTrue(Directory.Exists(this.GitDirectory)); } [Test] public void FailsIfNotAGitRepository() { - Assert.Throws(() => { GitIssue.IssueManager.Init(TestDirectory); }); + Assert.Throws(() => { IssueManager.Init(this.TestDirectory); }); } } } diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Locate.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Locate.cs index a83bcb3..e87452a 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Locate.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Locate.cs @@ -14,54 +14,54 @@ public class Constructor : IssueManagerTests [Test] public void FailsIfConfigMissing() { - Initialize(TestDirectory, true, true, false); - File.Delete(Path.Combine(TestDirectory, Paths.IssueRootFolderName, Paths.ConfigFileName)); - Assert.Throws(() => { Sut = IssueManager.Open(TestDirectory); }); + this.Initialize(this.TestDirectory, true, true, false); + File.Delete(Path.Combine(this.TestDirectory, Paths.IssueRootFolderName, Paths.ConfigFileName)); + Assert.Throws(() => { this.Sut = IssueManager.Open(this.TestDirectory); }); } [Test] public void FailsIfDirectoryMissing() { - Initialize(TestDirectory, true, true, false); - Directory.Delete(Path.Combine(TestDirectory, Paths.IssueRootFolderName), true); - Assert.Throws(() => { Sut = IssueManager.Open(TestDirectory); }); + this.Initialize(this.TestDirectory, true, true, false); + Directory.Delete(Path.Combine(this.TestDirectory, Paths.IssueRootFolderName), true); + Assert.Throws(() => { this.Sut = IssueManager.Open(this.TestDirectory); }); } [Test] public void FailsIfGitDirectoryMissing() { - Initialize(TestDirectory); - Directory.Delete(Path.Combine(TestDirectory, Paths.GitFolderName), true); + this.Initialize(this.TestDirectory); + Directory.Delete(Path.Combine(this.TestDirectory, Paths.GitFolderName), true); Assert.Throws(() => { - Sut = IssueManager.Open(TestDirectory); + this.Sut = IssueManager.Open(this.TestDirectory); }); } [Test] public void SucceedsFromCurrentDirectory() { - using (new Helpers.EnvironmentCurrentDirectory(TestDirectory)) + using (new Helpers.EnvironmentCurrentDirectory(this.TestDirectory)) { - Initialize(TestDirectory); - Assert.That(Sut.Root.RootPath, Is.EqualTo(TestDirectory)); + this.Initialize(this.TestDirectory); + Assert.That(this.Sut.Root.RootPath, Is.EqualTo(this.TestDirectory)); } } [Test] public void SucceedsFromSpecifiedDirectory() { - Initialize(TestDirectory); - Assert.That(Sut.Root.RootPath, Is.EqualTo(TestDirectory)); + this.Initialize(this.TestDirectory); + Assert.That(this.Sut.Root.RootPath, Is.EqualTo(this.TestDirectory)); } [Test] public void SucceedsWithDifferentName() { - var name = "Specifications"; - Initialize(TestDirectory, name); - Assert.That(Sut.Root.RootPath, Is.EqualTo(TestDirectory)); - Assert.That(Directory.Exists(Path.Combine(TestDirectory, name)), Is.True); + string name = "Specifications"; + this.Initialize(this.TestDirectory, name); + Assert.That(this.Sut.Root.RootPath, Is.EqualTo(this.TestDirectory)); + Assert.That(Directory.Exists(Path.Combine(this.TestDirectory, name)), Is.True); } } } diff --git a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Update.cs b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Update.cs index 7a05b27..f826b54 100644 --- a/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Update.cs +++ b/src/GitIssue.Tests/IssueManagerTests/IssueManagerTests_Update.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using GitIssue.Issues; using NUnit.Framework; namespace GitIssue.Tests.IssueManagerTests @@ -14,27 +15,27 @@ public class Update : IssueManagerTests [Test] public async Task SetsUpdateDate() { - Initialize(TestDirectory); - var create = await Sut - .CreateAsync(nameof(SetsUpdateDate), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut + .CreateAsync(nameof(Update.SetsUpdateDate), string.Empty) .WithSafeResultAsync(); await create.Result.SaveAsync(); - var find = Sut.Find(i => i.Key == create.Result.Key).ToArray(); - var issue = find[0]; + IIssue[] find = this.Sut.Find(i => i.Key == create.Result.Key).ToArray(); + IIssue issue = find[0]; Assert.That((DateTime)issue.Updated, Is.EqualTo(DateTime.Now).Within(TimeSpan.FromSeconds(2))); } [Test] public async Task UpdatesTitle() { - Initialize(TestDirectory); - var create = await Sut - .CreateAsync(nameof(UpdatesTitle), string.Empty) + this.Initialize(this.TestDirectory); + SafeResult create = await this.Sut + .CreateAsync(nameof(Update.UpdatesTitle), string.Empty) .WithSafeResultAsync(); create.Result.Title = "Updated"; await create.Result.SaveAsync(); - var find = Sut.Find(i => i.Key == create.Result.Key).ToArray(); - var issue = find[0]; + IIssue[] find = this.Sut.Find(i => i.Key == create.Result.Key).ToArray(); + IIssue issue = find[0]; Assert.That(issue.Title, Is.EqualTo("Updated")); } } diff --git a/src/GitIssue.Tests/Moqs.cs b/src/GitIssue.Tests/Moqs.cs index c0fbff8..2512f58 100644 --- a/src/GitIssue.Tests/Moqs.cs +++ b/src/GitIssue.Tests/Moqs.cs @@ -9,48 +9,69 @@ namespace GitIssue.Tests { public static class Moqs { - private delegate void MockOutDelegate(T1 s, out T2 value); + public static IReadOnlyIssue Issue + { + get + { + Mock moq = new Mock(MockBehavior.Strict); + return moq.Object; + } + } + + public static IIssueManager IssueManager + { + get + { + Mock moq = new Mock(MockBehavior.Strict); + return moq.Object; + } + } public static void AddField(this Dictionary dict, string key, string value) { - var fieldKey = FieldKey.Create(key); - var field = CreateField(key, value); + FieldKey fieldKey = FieldKey.Create(key); + IField field = Moqs.CreateField(key, value); dict.Add(fieldKey, field); } public static IField CreateField(FieldKey key, string value) { - var moq = new Mock(MockBehavior.Strict); + Mock moq = new Mock(MockBehavior.Strict); moq.Setup(f => f.Key).Returns(key); moq.Setup(f => f.ToString()).Returns(value); moq.Setup(f => f.GetHashCode()).Returns(value.GetHashCode()); return moq.Object; } - public static IssueKey CreateIssueKey() => IssueKey.Create(Helpers.GetRandomString()); - - public static IReadOnlyIssue CreateIssue(string title) => CreateIssue(title, string.Empty); + public static IReadOnlyIssue CreateIssue(string title) + { + return Moqs.CreateIssue(title, string.Empty); + } - public static IReadOnlyIssue CreateIssue(string title, string description) => - CreateIssue(CreateIssueKey(), title, description); + public static IReadOnlyIssue CreateIssue(string title, string description) + { + return Moqs.CreateIssue(Moqs.CreateIssueKey(), title, description); + } - public static IReadOnlyIssue CreateIssue(IssueKey key, string title, string description) => - CreateIssue(key, title, description, DateTime.Now, DateTime.Now); + public static IReadOnlyIssue CreateIssue(IssueKey key, string title, string description) + { + return Moqs.CreateIssue(key, title, description, DateTime.Now, DateTime.Now); + } public static IReadOnlyIssue CreateIssue(IssueKey key, string title, string description, DateTime created, DateTime updated) { - var fields = new Dictionary(); - fields.AddField(nameof(IIssue.Key), key); + Dictionary fields = new Dictionary(); + fields.AddField(nameof(IReadOnlyIssue.Key), key); fields.AddField(nameof(IIssue.Title), title); fields.AddField(nameof(IIssue.Description), description); fields.AddField(nameof(IIssue.Created), created.ToString(CultureInfo.InvariantCulture)); fields.AddField(nameof(IIssue.Updated), updated.ToString(CultureInfo.InvariantCulture)); - return CreateIssue(fields); + return Moqs.CreateIssue(fields); } public static IReadOnlyIssue CreateIssue(IReadOnlyDictionary fields) { - var moq = new Mock(MockBehavior.Strict); + Mock moq = new Mock(MockBehavior.Strict); FieldKey? tryGetFieldKey = null; IField? tryGetResult = null; @@ -58,7 +79,7 @@ public static IReadOnlyIssue CreateIssue(IReadOnlyDictionary f moq.Setup(i => i[It.IsAny()]).Returns((FieldKey key) => fields[key]); moq.Setup(i => i.TryGetValue(It.IsAny(), out tryGetResult)) - .Callback(new MockOutDelegate((FieldKey key, out IField output) => + .Callback(new MockOutDelegate((key, out output) => { tryGetFieldKey = key; fields.TryGetValue(key, out tryGetResult); @@ -70,48 +91,48 @@ public static IReadOnlyIssue CreateIssue(IReadOnlyDictionary f { return false; } + return fields.ContainsKey(tryGetFieldKey.Value); }); moq.Setup(i => i.GetEnumerator()).Returns(fields.GetEnumerator()); - foreach (var field in fields.Values) + foreach (IField field in fields.Values) { - if (field.Key == nameof(IIssue.Key)) + if (field.Key == nameof(IReadOnlyIssue.Key)) + { moq.Setup(i => i.Key).Returns(IssueKey.Create(field.ToString())); + } if (field.Key == nameof(IIssue.Title)) + { moq.Setup(i => i.Title).Returns(field.ToString() ?? string.Empty); + } if (field.Key == nameof(IIssue.Description)) + { moq.Setup(i => i.Description).Returns(field.ToString() ?? string.Empty); + } if (field.Key == nameof(IIssue.Created)) + { moq.Setup(i => i.Created).Returns(DateTime.Parse(field.ToString() ?? string.Empty)); + } if (field.Key == nameof(IIssue.Updated)) + { moq.Setup(i => i.Updated).Returns(DateTime.Parse(field.ToString() ?? string.Empty)); + } } return moq.Object; } - public static IReadOnlyIssue Issue + public static IssueKey CreateIssueKey() { - get - { - var moq = new Mock(MockBehavior.Strict); - return moq.Object; - } + return IssueKey.Create(Helpers.GetRandomString()); } - public static IIssueManager IssueManager - { - get - { - var moq = new Mock(MockBehavior.Strict); - return moq.Object; - } - } + private delegate void MockOutDelegate(T1 s, out T2 value); } -} +} \ No newline at end of file diff --git a/src/GitIssue.Tests/SyncTests/FileSyncTests.cs b/src/GitIssue.Tests/SyncTests/FileSyncTests.cs index 32b8bfa..1bb60b5 100644 --- a/src/GitIssue.Tests/SyncTests/FileSyncTests.cs +++ b/src/GitIssue.Tests/SyncTests/FileSyncTests.cs @@ -5,6 +5,5 @@ namespace GitIssue.Tests.SyncTests [TestFixture] public partial class FileSyncTests : IssueManagerTests.IssueManagerTests { - } } \ No newline at end of file diff --git a/src/GitIssue.Tests/SyncTests/FileSyncTests_Import.cs b/src/GitIssue.Tests/SyncTests/FileSyncTests_Import.cs index 69f0085..2a4be92 100644 --- a/src/GitIssue.Tests/SyncTests/FileSyncTests_Import.cs +++ b/src/GitIssue.Tests/SyncTests/FileSyncTests_Import.cs @@ -14,18 +14,14 @@ public class Import : FileSyncTests [Test] public async Task ImportsIssue() { - var issue_file = Path.Combine(TestContext.CurrentContext.TestDirectory, + string issue_file = Path.Combine(TestContext.CurrentContext.TestDirectory, "SyncTests", "ImportFiles", "jira-issue.json"); - var config_file = Path.Combine(TestContext.CurrentContext.TestDirectory, + string config_file = Path.Combine(TestContext.CurrentContext.TestDirectory, "SyncTests", "ImportFiles", "jira-config.json"); - Initialize(TestDirectory); - var importer = new FileImporter(this.Sut) - { - Configuration = await SyncConfiguration.ReadAsync(config_file), - ImportPath = issue_file, - }; + this.Initialize(this.TestDirectory); + FileImporter importer = new FileImporter(this.Sut) { Configuration = await SyncConfiguration.ReadAsync(config_file), ImportPath = issue_file }; await importer.Import(); } } diff --git a/src/GitIssue.Tests/SyncTests/ImportFiles/jira-issues.json b/src/GitIssue.Tests/SyncTests/ImportFiles/jira-issues.json index b66c6ea..5cf4418 100644 --- a/src/GitIssue.Tests/SyncTests/ImportFiles/jira-issues.json +++ b/src/GitIssue.Tests/SyncTests/ImportFiles/jira-issues.json @@ -3,7 +3,7 @@ "externalName" : "Project Name", "name" : "Project Name", "key" : "PNKEY", - "lead" : "lennonco", + "lead" : "Bob", "type" : "software", "assigneeType" : 3, "versions" : [ { diff --git a/src/GitIssue.Tests/SyncTests/SyncConfigurationTests.cs b/src/GitIssue.Tests/SyncTests/SyncConfigurationTests.cs index a8779df..1b589e9 100644 --- a/src/GitIssue.Tests/SyncTests/SyncConfigurationTests.cs +++ b/src/GitIssue.Tests/SyncTests/SyncConfigurationTests.cs @@ -3,8 +3,7 @@ namespace GitIssue.Tests.SyncTests { [TestFixture] - public partial class SyncConfigurationTests + public class SyncConfigurationTests { - } } \ No newline at end of file diff --git a/src/GitIssue.Tests/TestBase.cs b/src/GitIssue.Tests/TestBase.cs index 18eca89..69fdb1f 100644 --- a/src/GitIssue.Tests/TestBase.cs +++ b/src/GitIssue.Tests/TestBase.cs @@ -4,34 +4,17 @@ namespace GitIssue.Tests { - public class TestsBase { - [SetUp] - public virtual void Setup() - { - TestDirectory = Helpers.GetTempDirectory(); - if (Directory.Exists(TestDirectory) == false) Directory.CreateDirectory(TestDirectory); - } - - protected virtual string TestDirectory { get; set; } = Helpers.GetTestDirectory(); + protected virtual string ConfigFile => Path.Combine(this.IssueDirectory, Paths.ConfigFileName); - protected virtual string GitDirectory => Path.Combine(TestDirectory, Paths.GitFolderName); + protected virtual string GitDirectory => Path.Combine(this.TestDirectory, Paths.GitFolderName); - protected virtual string IssueDirectory => Path.Combine(TestDirectory, Paths.IssueRootFolderName); - - protected virtual string ConfigFile => Path.Combine(IssueDirectory, Paths.ConfigFileName); + protected virtual string IssueDirectory => Path.Combine(this.TestDirectory, Paths.IssueRootFolderName); protected virtual IIssueManager Manager { get; set; } = null!; - [OneTimeSetUp] - public virtual void OneTimeSetup() - { - TestDirectory = Helpers.GetTestDirectory(); - if (Directory.Exists(TestDirectory)) - Directory.Delete(TestDirectory, true); - Directory.CreateDirectory(TestDirectory); - } + protected virtual string TestDirectory { get; set; } = Helpers.GetTestDirectory(); public void Initialize( string directory, @@ -39,9 +22,20 @@ public void Initialize( bool initIssue = true, bool initSut = true) { - if (initGit) Repository.Init(directory); - if (initIssue) GitIssue.IssueManager.Init(directory); - if (initSut) Manager = GitIssue.IssueManager.Open(TestDirectory); + if (initGit) + { + Repository.Init(directory); + } + + if (initIssue) + { + IssueManager.Init(directory); + } + + if (initSut) + { + this.Manager = IssueManager.Open(this.TestDirectory); + } } public void Initialize( @@ -51,9 +45,42 @@ public void Initialize( bool initIssue = true, bool initSut = true) { - if (initGit) Repository.Init(directory); - if (initIssue) IssueManager.Init(directory, name); - if (initSut) Manager = IssueManager.Open(TestDirectory, name); + if (initGit) + { + Repository.Init(directory); + } + + if (initIssue) + { + IssueManager.Init(directory, name); + } + + if (initSut) + { + this.Manager = IssueManager.Open(this.TestDirectory, name); + } + } + + [OneTimeSetUp] + public virtual void OneTimeSetup() + { + this.TestDirectory = Helpers.GetTestDirectory(); + if (Directory.Exists(this.TestDirectory)) + { + Directory.Delete(this.TestDirectory, true); + } + + Directory.CreateDirectory(this.TestDirectory); + } + + [SetUp] + public virtual void Setup() + { + this.TestDirectory = Helpers.GetTempDirectory(); + if (!Directory.Exists(this.TestDirectory)) + { + Directory.CreateDirectory(this.TestDirectory); + } } } } \ No newline at end of file diff --git a/src/GitIssue.Tests/ValueTests/EmailValueTests.cs b/src/GitIssue.Tests/ValueTests/EmailValueTests.cs index ceaec11..ccd30e6 100644 --- a/src/GitIssue.Tests/ValueTests/EmailValueTests.cs +++ b/src/GitIssue.Tests/ValueTests/EmailValueTests.cs @@ -14,13 +14,13 @@ public class TypeConverter : EmailValueTests [TestCaseSource(typeof(CanConvertTestCases))] public bool CanConvert(Type type) { - return HasConverter(type); + return this.HasConverter(type); } [TestCaseSource(typeof(ConvertFromStringTestCases))] public Email Convert(object value) { - return (Email)UseConverter(value); + return (Email)this.UseConverter(value); } public class CanConvertTestCases : ValueTestCases @@ -56,7 +56,7 @@ public class TryParse : EmailValueTests [TestCaseSource(typeof(TryParseTestCases))] public bool Test(string value, Email expected) { - if (Email.TryParse(value, out var result)) + if (Email.TryParse(value, out Email result)) { Assert.That(expected, Is.EqualTo(result)); return true; diff --git a/src/GitIssue.Tests/ValueTests/LabelValueTests.cs b/src/GitIssue.Tests/ValueTests/LabelValueTests.cs index 2b71174..5b2681b 100644 --- a/src/GitIssue.Tests/ValueTests/LabelValueTests.cs +++ b/src/GitIssue.Tests/ValueTests/LabelValueTests.cs @@ -14,13 +14,13 @@ public class TypeConverter : LabelValueTests [TestCaseSource(typeof(CanConvertTestCases))] public bool CanConvert(Type type) { - return HasConverter(type); + return this.HasConverter(type); } [TestCaseSource(typeof(ConvertFromStringTestCases))] public Label Convert(object value) { - return (Label)UseConverter(value); + return (Label)this.UseConverter(value); } public class CanConvertTestCases : ValueTestCases @@ -65,7 +65,7 @@ public class TryParse : LabelValueTests [TestCaseSource(typeof(TryParseTestCases))] public bool Test(string value, Label expected) { - if (Label.TryParse(value, out var result)) + if (Label.TryParse(value, out Label result)) { Assert.That(expected, Is.EqualTo(result)); return true; diff --git a/src/GitIssue.Tests/ValueTests/NumberValueTests.cs b/src/GitIssue.Tests/ValueTests/NumberValueTests.cs index 962ea21..9906de4 100644 --- a/src/GitIssue.Tests/ValueTests/NumberValueTests.cs +++ b/src/GitIssue.Tests/ValueTests/NumberValueTests.cs @@ -14,13 +14,13 @@ public class TypeConverter : NumberValueTests [TestCaseSource(typeof(CanConvertTestCases))] public bool CanConvert(Type type) { - return HasConverter(type); + return this.HasConverter(type); } [TestCaseSource(typeof(ConvertFromStringTestCases))] public Number Convert(object value) { - return (Number)UseConverter(value); + return (Number)this.UseConverter(value); } public class CanConvertTestCases : ValueTestCases @@ -76,7 +76,7 @@ public class TryParse : NumberValueTests [TestCaseSource(typeof(TryParseTestCases))] public bool Test(string value, Number expected) { - if (Number.TryParse(value, out var result)) + if (Number.TryParse(value, out Number result)) { Assert.That(expected, Is.EqualTo(result)); return true; diff --git a/src/GitIssue.Tests/ValueTests/SignatureValueTests.cs b/src/GitIssue.Tests/ValueTests/SignatureValueTests.cs index 2f6e000..ee2415d 100644 --- a/src/GitIssue.Tests/ValueTests/SignatureValueTests.cs +++ b/src/GitIssue.Tests/ValueTests/SignatureValueTests.cs @@ -14,13 +14,13 @@ public class TypeConverter : SignatureValueTests [TestCaseSource(typeof(CanConvertTestCases))] public bool CanConvert(Type type) { - return HasConverter(type); + return this.HasConverter(type); } [TestCaseSource(typeof(ConvertFromStringTestCases))] public Signature Convert(object value) { - return (Signature)UseConverter(value); + return (Signature)this.UseConverter(value); } public class CanConvertTestCases : ValueTestCases @@ -56,7 +56,7 @@ public class TryParse : SignatureValueTests [TestCaseSource(typeof(TryParseTestCases))] public bool Test(string value, Signature expected) { - if (Signature.TryParse(value, out var result)) + if (Signature.TryParse(value, out Signature result)) { Assert.That(expected, Is.EqualTo(result)); return true; diff --git a/src/GitIssue.Tests/ValueTests/StringValueTests.cs b/src/GitIssue.Tests/ValueTests/StringValueTests.cs index 6bb96d2..5fc8615 100644 --- a/src/GitIssue.Tests/ValueTests/StringValueTests.cs +++ b/src/GitIssue.Tests/ValueTests/StringValueTests.cs @@ -15,13 +15,13 @@ public class TypeConverter : StringValueTests [TestCaseSource(typeof(CanConvertTestCases))] public bool CanConvert(Type type) { - return HasConverter(type); + return this.HasConverter(type); } [TestCaseSource(typeof(ConvertFromStringTestCases))] public String Convert(object value) { - return (String)UseConverter(value); + return (String)this.UseConverter(value); } public class CanConvertTestCases : ValueTestCases @@ -57,7 +57,7 @@ public class TryParse : StringValueTests [TestCaseSource(typeof(TryParseTestCases))] public bool Test(string value, String expected) { - if (String.TryParse(value, out var result)) + if (String.TryParse(value, out String result)) { Assert.That(expected, Is.EqualTo(result)); return true; @@ -80,7 +80,7 @@ public override IEnumerator GetEnumerator() [TestFixture] public class Item : StringValueTests { - [TestCaseSource(typeof(Item.GetItemTestCases))] + [TestCaseSource(typeof(GetItemTestCases))] public string Tests(String str) { return base.GetItem(str); @@ -100,7 +100,7 @@ public override IEnumerator GetEnumerator() [TestFixture] public class ToJson : StringValueTests { - [TestCaseSource(typeof(ToJson.ConvertToJsonTestCases))] + [TestCaseSource(typeof(ConvertToJsonTestCases))] public string Tests(String str) { return base.ConvertToJson(str); @@ -120,7 +120,7 @@ public override IEnumerator GetEnumerator() [TestFixture] public new class Equals : StringValueTests { - [TestCaseSource(typeof(Equals.EqualsTestCases))] + [TestCaseSource(typeof(EqualsTestCases))] public bool Tests(object first, object second) { return base.Equals(first, second); diff --git a/src/GitIssue.Tests/ValueTests/UrlTestCases.cs b/src/GitIssue.Tests/ValueTests/UrlTestCases.cs index 17ea660..fb49a47 100644 --- a/src/GitIssue.Tests/ValueTests/UrlTestCases.cs +++ b/src/GitIssue.Tests/ValueTests/UrlTestCases.cs @@ -14,13 +14,13 @@ public class TypeConverter : UrlValueTests [TestCaseSource(typeof(CanConvertTestCases))] public bool CanConvert(Type type) { - return HasConverter(type); + return this.HasConverter(type); } [TestCaseSource(typeof(ConvertFromStringTestCases))] public Url Convert(object value) { - return (Url)UseConverter(value); + return (Url)this.UseConverter(value); } public class CanConvertTestCases : ValueTestCases @@ -56,7 +56,7 @@ public class TryParse : UrlValueTests [TestCaseSource(typeof(TryParseTestCases))] public bool Test(string value, Url expected) { - if (Url.TryParse(value, out var result)) + if (Url.TryParse(value, out Url result)) { Assert.That(expected, Is.EqualTo(result)); return true; diff --git a/src/GitIssue.Tests/ValueTests/ValueTests.cs b/src/GitIssue.Tests/ValueTests/ValueTests.cs index 271b9e4..886822c 100644 --- a/src/GitIssue.Tests/ValueTests/ValueTests.cs +++ b/src/GitIssue.Tests/ValueTests/ValueTests.cs @@ -10,45 +10,45 @@ namespace GitIssue.Tests.ValueTests { public abstract class ValueTests where TValue : IValue { + public new virtual bool Equals(object first, object second) + { + bool objEquals = first.Equals(second); + bool hashEquals = first?.GetHashCode() == second?.GetHashCode(); + Assert.That(objEquals, Is.EqualTo(hashEquals)); + return objEquals; + } + public bool HasConverter(Type type) { - var converter = TypeDescriptor.GetConverter(typeof(TValue)); + TypeConverter converter = TypeDescriptor.GetConverter(typeof(TValue)); return converter.CanConvertFrom(type); } public bool HasConverter() { - return HasConverter(typeof(TIn)); + return this.HasConverter(typeof(TIn)); } public object UseConverter(object input) { - var converter = TypeDescriptor.GetConverter(typeof(TValue)); + TypeConverter converter = TypeDescriptor.GetConverter(typeof(TValue)); if (converter.CanConvertFrom(input.GetType())) { - var result = converter.ConvertFrom(input); - if(result != null) + object? result = converter.ConvertFrom(input); + if (result != null) { return result; } } + Assert.Fail($"Failed to convert from {input.GetType()} to {typeof(TValue)}"); - return default!; + return default(object)!; } public TValue UseConverter(TIn input) { - return (TValue)UseConverter((object)input!); + return (TValue)this.UseConverter((object)input!); } - - public new virtual bool Equals(object first, object second) - { - var objEquals = first.Equals(second); - var hashEquals = first?.GetHashCode() == second?.GetHashCode(); - Assert.That(objEquals, Is.EqualTo(hashEquals)); - return objEquals; - } - } public abstract class ValueTests : ValueTests @@ -84,7 +84,7 @@ public abstract class ValueTestCases : IEnumerable IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); + return this.GetEnumerator(); } } } \ No newline at end of file diff --git a/src/GitIssue.Tool/App.config b/src/GitIssue.Tool/App.config deleted file mode 100644 index d474aa7..0000000 --- a/src/GitIssue.Tool/App.config +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Add/AddCommand.cs b/src/GitIssue.Tool/Commands/Add/AddCommand.cs index 8006b5b..a369c33 100644 --- a/src/GitIssue.Tool/Commands/Add/AddCommand.cs +++ b/src/GitIssue.Tool/Commands/Add/AddCommand.cs @@ -4,6 +4,7 @@ using GitIssue.Fields; using GitIssue.Fields.Array; using GitIssue.Formatters; +using GitIssue.Issues; using Serilog; namespace GitIssue.Tool.Commands.Add @@ -26,9 +27,9 @@ public AddCommand(IIssueManager manager, ILogger logger) /// public override async Task Exec(AddOptions options) { - var formatter = IssueFormatter.Simple; + IssueFormatter formatter = IssueFormatter.Simple; - var issue = await manager + IIssue? issue = await this.manager .FindAsync(i => i.Key.ToString() == options.Key) .FirstOrDefaultAsync(); @@ -44,17 +45,17 @@ public override async Task Exec(AddOptions options) return; } - var key = FieldKey.Create(options.Field); + FieldKey key = FieldKey.Create(options.Field); if (!issue.ContainsKey(key)) { this.logger?.Error($"Field \"{key}\" does not exist on issue \"{issue.Key}\""); return; } - var field = issue[key]; + IField field = issue[key]; if (field is IArrayField arrayField) { - if (arrayField.TryParse(options.Add, out var value)) + if (arrayField.TryParse(options.Add, out object? value)) { if (!arrayField.Contains(value)) { diff --git a/src/GitIssue.Tool/Commands/Add/AddOptions.cs b/src/GitIssue.Tool/Commands/Add/AddOptions.cs index 2671089..63af4c2 100644 --- a/src/GitIssue.Tool/Commands/Add/AddOptions.cs +++ b/src/GitIssue.Tool/Commands/Add/AddOptions.cs @@ -6,11 +6,11 @@ namespace GitIssue.Tool.Commands.Add [Verb(nameof(CommandType.Add), HelpText = "Add a value to an existing field on an existing issue")] public class AddOptions : EditorOptions { - [Value(2, MetaName = "Field Key", HelpText = "The field to edit", Required = false)] - public string Field { get; set; } = string.Empty; - [Value(3, MetaName = "Value", HelpText = "The text to update the field with", Required = false)] public string Add { get; set; } = string.Empty; + + [Value(2, MetaName = "Field Key", HelpText = "The field to edit", Required = false)] + public string Field { get; set; } = string.Empty; } #pragma warning restore 1591 } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Changes/ChangesCommand.cs b/src/GitIssue.Tool/Commands/Changes/ChangesCommand.cs index 0774d15..9ecbc99 100644 --- a/src/GitIssue.Tool/Commands/Changes/ChangesCommand.cs +++ b/src/GitIssue.Tool/Commands/Changes/ChangesCommand.cs @@ -25,7 +25,7 @@ public ChangesCommand(IIssueManager manager, ILogger logger) /// public override Task Exec(ChangesOptions options) { - Console.Write(manager.Changes.GenerateComments()); + Console.Write(this.manager.Changes.GenerateComments()); return Task.CompletedTask; } } diff --git a/src/GitIssue.Tool/Commands/Changes/ChangesOptions.cs b/src/GitIssue.Tool/Commands/Changes/ChangesOptions.cs index e863758..6dd9a6e 100644 --- a/src/GitIssue.Tool/Commands/Changes/ChangesOptions.cs +++ b/src/GitIssue.Tool/Commands/Changes/ChangesOptions.cs @@ -6,7 +6,6 @@ namespace GitIssue.Tool.Commands.Changes [Verb(nameof(CommandType.Changes), HelpText = "Shows the change log")] public class ChangesOptions : Options { - } #pragma warning restore 1591 } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/CommandType.cs b/src/GitIssue.Tool/Commands/CommandType.cs index 28e710a..16516b8 100644 --- a/src/GitIssue.Tool/Commands/CommandType.cs +++ b/src/GitIssue.Tool/Commands/CommandType.cs @@ -84,6 +84,6 @@ public enum CommandType /// /// Shows the current status /// - Changes + Changes, } } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Comment/CommentCommand.cs b/src/GitIssue.Tool/Commands/Comment/CommentCommand.cs index 1c54e35..eb5efd1 100644 --- a/src/GitIssue.Tool/Commands/Comment/CommentCommand.cs +++ b/src/GitIssue.Tool/Commands/Comment/CommentCommand.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using GitIssue.Fields; using GitIssue.Fields.Array; +using GitIssue.Issues; using Serilog; using String = GitIssue.Values.String; @@ -13,12 +14,12 @@ namespace GitIssue.Tool.Commands.Comment /// public class CommentCommand : Command { - private readonly static string CommentField = "Comments"; - - private readonly ILogger logger; + private static readonly string CommentField = "Comments"; private readonly IEditor editor; + private readonly ILogger logger; + private readonly IIssueManager manager; public CommentCommand(IIssueManager manager, IEditor editor, ILogger logger) @@ -31,9 +32,9 @@ public CommentCommand(IIssueManager manager, IEditor editor, ILogger logger) /// public override async Task Exec(CommentOptions options) { - var formatter = TerminalFormatter.Detailed; + TerminalFormatter formatter = TerminalFormatter.Detailed; - var issue = await manager + IIssue? issue = await this.manager .FindAsync(i => i.Key.ToString() == options.Key) .FirstOrDefaultAsync(); @@ -45,7 +46,7 @@ public override async Task Exec(CommentOptions options) if (string.IsNullOrEmpty(options.Comment)) { - options.Comment = await editor.Edit($"Add a comment to {issue.Key} below", ""); + options.Comment = await this.editor.Edit($"Add a comment to {issue.Key} below", ""); } if (string.IsNullOrEmpty(options.Comment)) @@ -54,12 +55,12 @@ public override async Task Exec(CommentOptions options) return; } - var key = FieldKey.Create(CommentField); - if (issue.TryGetValue(key, out var field)) + FieldKey key = FieldKey.Create(CommentCommand.CommentField); + if (issue.TryGetValue(key, out IField? field)) { if (field is IArrayField arrayField) { - if (arrayField.TryParse(options.Comment, out var value)) + if (arrayField.TryParse(options.Comment, out object? value)) { arrayField.Add(value); await issue.SaveAsync(); @@ -69,6 +70,7 @@ public override async Task Exec(CommentOptions options) { this.logger?.Error($"Comment \"{options.Comment}\" is not valid"); } + arrayField.Add(String.Parse(options.Comment)); } } diff --git a/src/GitIssue.Tool/Commands/Commit/CommitCommand.cs b/src/GitIssue.Tool/Commands/Commit/CommitCommand.cs index dc2079b..06a8265 100644 --- a/src/GitIssue.Tool/Commands/Commit/CommitCommand.cs +++ b/src/GitIssue.Tool/Commands/Commit/CommitCommand.cs @@ -22,10 +22,10 @@ public CommitCommand(IIssueManager manager, ILogger logger) /// public override async Task Exec(CommitOptions options) { - var result = await manager.CommitAsync(); + bool result = await this.manager.CommitAsync(); if (result) { - Console.WriteLine($"Committed changes in {manager.Root.Name}, see log for details"); + Console.WriteLine($"Committed changes in {this.manager.Root.Name}, see log for details"); } } } diff --git a/src/GitIssue.Tool/Commands/Create/CreateCommand.cs b/src/GitIssue.Tool/Commands/Create/CreateCommand.cs index 7b4974d..3c943e6 100644 --- a/src/GitIssue.Tool/Commands/Create/CreateCommand.cs +++ b/src/GitIssue.Tool/Commands/Create/CreateCommand.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using GitIssue.Fields; using GitIssue.Fields.Value; using GitIssue.Formatters; using GitIssue.Issues; @@ -15,15 +16,14 @@ namespace GitIssue.Tool.Commands.Create /// public class CreateCommand : Command { - private Lazy formatter = new Lazy(() => IssueFormatter.Detailed); - - private readonly ILogger logger; + private readonly IIssueConfiguration configuration; private readonly IEditor editor; - private readonly IIssueManager manager; + private readonly ILogger logger; - private readonly IIssueConfiguration configuration; + private readonly IIssueManager manager; + private Lazy formatter = new Lazy(() => IssueFormatter.Detailed); public CreateCommand(IIssueManager manager, IIssueConfiguration configuration, IEditor editor, ILogger logger) { @@ -39,18 +39,18 @@ public override async Task Exec(CreateOptions options) IIssue issue; if (string.IsNullOrEmpty(options.Title)) { - var fields = this.configuration.Fields - .Where(f => f.Key != nameof(IIssue.Key)) + IField[] fields = this.configuration.Fields + .Where(f => f.Key != nameof(IReadOnlyIssue.Key)) .Where(f => f.Key != nameof(IIssue.Created)) .Where(f => f.Key != nameof(IIssue.Updated)) .Select(f => f.Value.CreateField(null!, f.Key)) .ToArray(); - await editor.Open(fields); + await this.editor.Open(fields); if (!(fields.FirstOrDefault(f => f.Key == nameof(IIssue.Title)) is IValueField title)) { - this.logger.Error($"Title is not a valid value field"); + this.logger.Error("Title is not a valid value field"); return; } @@ -62,21 +62,21 @@ public override async Task Exec(CreateOptions options) if (string.IsNullOrEmpty(value.Item)) { - this.logger.Error($"A valid title must be provided"); + this.logger.Error("A valid title must be provided"); } - issue = await manager.CreateAsync(value.Item); - foreach (var field in fields) + issue = await this.manager.CreateAsync(value.Item); + foreach (IField field in fields) { issue.SetField(field.Key).WithField(field); } } else { - issue = await manager.CreateAsync(options.Title, options.Description); + issue = await this.manager.CreateAsync(options.Title, options.Description); } - if (options.Track || options.Tracked == TrackedIssue.None) + if (options.Track || (options.Tracked == TrackedIssue.None)) { options.Tracked = new TrackedIssue(issue.Key); await options.Tracked.SaveAsync(Path.Combine(options.Path, options.Name, options.Tracking), this.logger); diff --git a/src/GitIssue.Tool/Commands/Create/CreateOptions.cs b/src/GitIssue.Tool/Commands/Create/CreateOptions.cs index 92a6dce..bf8d6d6 100644 --- a/src/GitIssue.Tool/Commands/Create/CreateOptions.cs +++ b/src/GitIssue.Tool/Commands/Create/CreateOptions.cs @@ -6,14 +6,14 @@ namespace GitIssue.Tool.Commands.Create [Verb(nameof(CommandType.Create), HelpText = "Creates a new issue")] public class CreateOptions : EditorOptions, ITrackedOptions { - [Option("Track", HelpText = "Track the new issue, even if another issue is already tracked", Required = false)] - public bool Track { get; set; } = false; + [Value(2, MetaName = "Description", HelpText = "The issue description", Required = false)] + public string Description { get; set; } = string.Empty; [Value(1, MetaName = "Title", HelpText = "The issue title", Required = false)] public string Title { get; set; } = string.Empty; - [Value(2, MetaName = "Description", HelpText = "The issue description", Required = false)] - public string Description { get; set; } = string.Empty; + [Option("Track", HelpText = "Track the new issue, even if another issue is already tracked", Required = false)] + public bool Track { get; set; } = false; } #pragma warning restore 1591 } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Delete/DeleteCommand.cs b/src/GitIssue.Tool/Commands/Delete/DeleteCommand.cs index 4bf99df..0e9fd41 100644 --- a/src/GitIssue.Tool/Commands/Delete/DeleteCommand.cs +++ b/src/GitIssue.Tool/Commands/Delete/DeleteCommand.cs @@ -24,7 +24,7 @@ public DeleteCommand(IIssueManager manager, ILogger logger) /// public override async Task Exec(DeleteOptions options) { - var result = await manager.DeleteAsync(options.Key); + bool result = await this.manager.DeleteAsync(options.Key); if (result) { Console.WriteLine($"Deleted issue '{options.Key}'"); diff --git a/src/GitIssue.Tool/Commands/Edit/EditCommand.cs b/src/GitIssue.Tool/Commands/Edit/EditCommand.cs index fd76f1b..db7239a 100644 --- a/src/GitIssue.Tool/Commands/Edit/EditCommand.cs +++ b/src/GitIssue.Tool/Commands/Edit/EditCommand.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using GitIssue.Fields; using GitIssue.Formatters; +using GitIssue.Issues; using LibGit2Sharp; using Serilog; @@ -13,9 +14,8 @@ namespace GitIssue.Tool.Commands.Edit /// public class EditCommand : Command { - private readonly ILogger logger; - private readonly IEditor editor; + private readonly ILogger logger; private readonly IIssueManager manager; @@ -26,18 +26,12 @@ public EditCommand(IIssueManager manager, IEditor editor, ILogger logger) this.logger = logger; } - private static bool TryGetEditor(IRepository repository, string key, out string config) - { - config = repository.Config.GetValueOrDefault(key); - return string.IsNullOrEmpty(config) == false; - } - /// public override async Task Exec(EditOptions options) { - var formatter = TerminalFormatter.Detailed; + TerminalFormatter formatter = TerminalFormatter.Detailed; - var issue = await manager + IIssue? issue = await this.manager .FindAsync(i => i.Key.ToString() == options.Key) .FirstOrDefaultAsync(); @@ -47,26 +41,30 @@ public override async Task Exec(EditOptions options) return; } - if (TryGetEditor(manager.Repository, "issues.editor", out string config)) + if (EditCommand.TryGetEditor(this.manager.Repository, "issues.editor", out string config)) { options.Editor = config; } - var updated = false; + bool updated = false; if (string.IsNullOrEmpty(options.Field)) { - await editor.Open(issue); + await this.editor.Open(issue); updated = true; } if (updated) { issue.Updated = DateTime.Now; - if (await issue.SaveAsync()) Console.WriteLine(issue.Format(formatter)); + if (await issue.SaveAsync()) + { + Console.WriteLine(issue.Format(formatter)); + } + return; } - var key = FieldKey.Create(options.Field); + FieldKey key = FieldKey.Create(options.Field); if (!issue.ContainsKey(key)) { this.logger.Error($"Field \"{key}\" does not exist on issue \"{issue.Key}\""); @@ -75,7 +73,7 @@ public override async Task Exec(EditOptions options) if (string.IsNullOrEmpty(options.Update)) { - await editor.Open(issue[key]); + await this.editor.Open(issue[key]); updated = true; } else if (issue[key].Update(options.Update)) @@ -86,8 +84,17 @@ public override async Task Exec(EditOptions options) if (updated) { issue.Updated = DateTime.Now; - if (await issue.SaveAsync()) Console.WriteLine(issue.Format(formatter)); + if (await issue.SaveAsync()) + { + Console.WriteLine(issue.Format(formatter)); + } } } + + private static bool TryGetEditor(IRepository repository, string key, out string config) + { + config = repository.Config.GetValueOrDefault(key); + return !string.IsNullOrEmpty(config); + } } } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Export/ExportCommand.cs b/src/GitIssue.Tool/Commands/Export/ExportCommand.cs index 963cc35..9c8dbe6 100644 --- a/src/GitIssue.Tool/Commands/Export/ExportCommand.cs +++ b/src/GitIssue.Tool/Commands/Export/ExportCommand.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; +using GitIssue.Fields; +using GitIssue.Issues; using GitIssue.Issues.Json; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -30,80 +33,84 @@ public override async Task Exec(ExportOptions options) switch (Path.GetExtension(options.Export)) { case ".json": - await exportJson(options); + await this.exportJson(options); break; case ".csv": - await exportCsv(options); + await this.exportCsv(options); break; } } - public async Task exportJson(ExportOptions options) - { - if (File.Exists(options.Export) && options.Overwrite == false) - { - this.logger.Error($"Export file {options.Export} exists, use '{nameof(ExportOptions.Overwrite)}' to force"); - return; - } - - JObject json = new JObject(); - await foreach (var issue in manager.FindAsync(i => true)) - { - if (issue is IJsonIssue jsonIssue) - { - json[issue.Key] = jsonIssue.ToJson(); - } - } - - await using var stream = new FileStream(options.Export, FileMode.Create, FileAccess.ReadWrite); - using JsonWriter writer = new JsonTextWriter(new StreamWriter(stream)); - var serializer = new JsonSerializer(); - serializer.Formatting = Formatting.Indented; - serializer.Serialize(writer, json); - - Console.WriteLine($"Exported {json.Count} issues to {options.Export}"); - } - public async Task exportCsv(ExportOptions options) { - if (File.Exists(options.Export) && options.Overwrite == false) + if (File.Exists(options.Export) && !options.Overwrite) { this.logger.Error($"Export file {options.Export} exists, use '{nameof(ExportOptions.Overwrite)}' to force"); return; } - await using var stream = new FileStream(options.Export, FileMode.Create, FileAccess.ReadWrite); + await using FileStream stream = new FileStream(options.Export, FileMode.Create, FileAccess.ReadWrite); using TextWriter writer = new StreamWriter(stream); StringBuilder builder = new StringBuilder(); - foreach (var field in manager.Configuration.Fields) + foreach (KeyValuePair field in this.manager.Configuration.Fields) { if (builder.Length != 0) { builder.Append(options.Separator); } + builder.Append(field.Key.ToString()); } + await writer.WriteLineAsync(builder.ToString()); int count = 0; - await foreach (var issue in manager.FindAsync(i => true)) + await foreach (IIssue issue in this.manager.FindAsync(i => true)) { builder.Clear(); - foreach (var field in manager.Configuration.Fields) + foreach (KeyValuePair field in this.manager.Configuration.Fields) { if (builder.Length != 0) { builder.Append(options.Separator); } - builder.Append(issue[field.Key].ToString()); + + builder.Append(issue[field.Key]); } + await writer.WriteLineAsync(builder.ToString()); count++; } Console.WriteLine($"Exported {count} issues to {options.Export}"); } + + public async Task exportJson(ExportOptions options) + { + if (File.Exists(options.Export) && !options.Overwrite) + { + this.logger.Error($"Export file {options.Export} exists, use '{nameof(ExportOptions.Overwrite)}' to force"); + return; + } + + JObject json = new JObject(); + await foreach (IIssue issue in this.manager.FindAsync(i => true)) + { + if (issue is IJsonIssue jsonIssue) + { + json[issue.Key] = jsonIssue.ToJson(); + } + } + + await using FileStream stream = new FileStream(options.Export, FileMode.Create, FileAccess.ReadWrite); + using JsonWriter writer = new JsonTextWriter(new StreamWriter(stream)); + JsonSerializer serializer = new JsonSerializer(); + serializer.Formatting = Formatting.Indented; + serializer.Serialize(writer, json); + + Console.WriteLine($"Exported {json.Count} issues to {options.Export}"); + } } } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Fields/FieldsCommand.cs b/src/GitIssue.Tool/Commands/Fields/FieldsCommand.cs index 25a7acc..c0a76c1 100644 --- a/src/GitIssue.Tool/Commands/Fields/FieldsCommand.cs +++ b/src/GitIssue.Tool/Commands/Fields/FieldsCommand.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Drawing; using System.Threading.Tasks; +using GitIssue.Fields; using Pastel; using Serilog; @@ -24,11 +26,12 @@ public FieldsCommand(IIssueManager manager, ILogger logger) /// public override Task Exec(FieldsOptions options) { - foreach (var kvp in manager.Configuration.Fields) + foreach (KeyValuePair kvp in this.manager.Configuration.Fields) { - var output = $"{kvp.Key.ToString().Pastel(Color.FromArgb(165, 229, 250))}: A '{kvp.Value.FieldType}' field with '{kvp.Value.ValueType}' values"; + string output = $"{kvp.Key.ToString().Pastel(Color.FromArgb(165, 229, 250))}: A '{kvp.Value.FieldType}' field with '{kvp.Value.ValueType}' values"; Console.WriteLine(output); } + return Task.CompletedTask; } } diff --git a/src/GitIssue.Tool/Commands/Find/FindCommand.cs b/src/GitIssue.Tool/Commands/Find/FindCommand.cs index 4df01d0..9650115 100644 --- a/src/GitIssue.Tool/Commands/Find/FindCommand.cs +++ b/src/GitIssue.Tool/Commands/Find/FindCommand.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using GitIssue.Formatters; using GitIssue.Issues; @@ -29,7 +30,7 @@ public override async Task Exec(FindOptions options) Func issueFilter; try { - var script = ScriptOptions.Default.AddReferences(typeof(Issue).Assembly); + ScriptOptions script = ScriptOptions.Default.AddReferences(typeof(Issue).Assembly); issueFilter = await CSharpScript.EvaluateAsync>(options.Linq, script); } catch (Exception e) @@ -38,9 +39,12 @@ public override async Task Exec(FindOptions options) return; } - var formatter = new TerminalFormatter(options.Format); - var find = manager.FindAsync(i => issueFilter.Invoke(i)); - await foreach (var issue in find) Console.WriteLine(issue.Format(formatter)); + TerminalFormatter formatter = new TerminalFormatter(options.Format); + IAsyncEnumerable find = this.manager.FindAsync(i => issueFilter.Invoke(i)); + await foreach (IIssue issue in find) + { + Console.WriteLine(issue.Format(formatter)); + } } } } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Find/FindOptions.cs b/src/GitIssue.Tool/Commands/Find/FindOptions.cs index d759291..0db6cdc 100644 --- a/src/GitIssue.Tool/Commands/Find/FindOptions.cs +++ b/src/GitIssue.Tool/Commands/Find/FindOptions.cs @@ -6,14 +6,14 @@ namespace GitIssue.Tool.Commands.Find [Verb(nameof(CommandType.Find), HelpText = "Finds an existing issue")] public class FindOptions : Options { - [Option("LinqName", HelpText = "The name of the issue in the linq expression", Required = false)] - public string LinqName { get; set; } = "i"; - [Option("Format", HelpText = "The format to output in", Required = false)] public string Format { get; set; } = "%Key %Title %Delta"; [Value(1, MetaName = "LINQ", HelpText = "The LINQ expression to use when matching", Required = false)] public string Linq { get; set; } = "i => true"; + + [Option("LinqName", HelpText = "The name of the issue in the linq expression", Required = false)] + public string LinqName { get; set; } = "i"; } #pragma warning restore 1591 } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Import/ImportCommand.cs b/src/GitIssue.Tool/Commands/Import/ImportCommand.cs index d5f3166..d534acb 100644 --- a/src/GitIssue.Tool/Commands/Import/ImportCommand.cs +++ b/src/GitIssue.Tool/Commands/Import/ImportCommand.cs @@ -30,20 +30,20 @@ public override async Task Exec(ImportOptions options) switch (Path.GetExtension(options.Import)) { case ".csv": - await importCsv(options); + await this.importCsv(options); break; } } public async Task importCsv(ImportOptions options) { - if (File.Exists(options.Import) == false) + if (!File.Exists(options.Import)) { this.logger.Error($"Import file {options.Import} does not exist"); return; } - await using var stream = new FileStream(options.Import, FileMode.Open, FileAccess.Read); + await using FileStream stream = new FileStream(options.Import, FileMode.Open, FileAccess.Read); using TextReader reader = new StreamReader(stream); string? header = await reader.ReadLineAsync(); @@ -52,9 +52,9 @@ public async Task importCsv(ImportOptions options) return; } - var fields = header.Split(options.Separator); - var mapping = new Dictionary(); - foreach (var field in manager.Configuration.Fields) + string[] fields = header.Split(options.Separator); + Dictionary mapping = new Dictionary(); + foreach (KeyValuePair field in this.manager.Configuration.Fields) { int? index = fields.Select((f, i) => new { Field = f, Index = i }) .Where(x => x.Field == field.Key.ToString()) @@ -67,15 +67,16 @@ public async Task importCsv(ImportOptions options) } } - if (mapping.ContainsKey("Key") == false) + if (!mapping.ContainsKey("Key")) { return; } - foreach (var map in mapping) + foreach (KeyValuePair map in mapping) { Console.WriteLine($"[{map.Value}] => {map.Key}"); } + Console.WriteLine(); int count = 0; @@ -87,23 +88,24 @@ public async Task importCsv(ImportOptions options) break; } - var import = line.Split(options.Separator); + string[] import = line.Split(options.Separator); if (import.Length != fields.Length) { continue; } IIssue? issue = null; - await foreach (var found in manager.FindAsync(i => i.Key == import[mapping["Key"]])) + await foreach (IIssue found in this.manager.FindAsync(i => i.Key == import[mapping["Key"]])) { issue = found; Console.WriteLine($"Updating {import[mapping["Key"]]}"); break; } + if (issue == null) { Console.WriteLine($"Creating {import[mapping["Key"]]}"); - issue = await manager.CreateAsync(import[mapping["Title"]]); + issue = await this.manager.CreateAsync(import[mapping["Title"]]); } count++; diff --git a/src/GitIssue.Tool/Commands/Init/InitCommand.cs b/src/GitIssue.Tool/Commands/Init/InitCommand.cs index 4a22a7a..6c6b95b 100644 --- a/src/GitIssue.Tool/Commands/Init/InitCommand.cs +++ b/src/GitIssue.Tool/Commands/Init/InitCommand.cs @@ -11,10 +11,10 @@ public class InitCommand : Command { public delegate void Initializer(); - private readonly ILogger logger; - private readonly Func factory; + private readonly ILogger logger; + private readonly Action onInit; public InitCommand(ILogger logger, Func factory, Initializer initializer) @@ -28,7 +28,7 @@ public InitCommand(ILogger logger, Func factory, Initializer init public override Task Exec(InitOptions options) { this.onInit(); - var manager = factory.Invoke(); + IIssueManager manager = this.factory.Invoke(); Console.WriteLine($"Initialized empty 'Issue' repository in {manager.Root.IssuesPath}"); return Task.CompletedTask; } diff --git a/src/GitIssue.Tool/Commands/Options.cs b/src/GitIssue.Tool/Commands/Options.cs index 33acef3..484e8ff 100644 --- a/src/GitIssue.Tool/Commands/Options.cs +++ b/src/GitIssue.Tool/Commands/Options.cs @@ -9,25 +9,24 @@ namespace GitIssue.Tool.Commands public class Options { - [Option("path", Required = false, HelpText = "The path to the working directory")] - public string Path { get; set; } = Environment.CurrentDirectory; - [Option("name", Required = false, HelpText = "The name of the issues folder")] public string Name { get; set; } = ".issues"; + [Option("path", Required = false, HelpText = "The path to the working directory")] + public string Path { get; set; } = Environment.CurrentDirectory; + [Option("tracking", Required = false, HelpText = "The tracking file to use")] public string Tracking { get; set; } = "tracking.json"; } public interface ITrackedOptions { - public string Path { get; set; } - - public string Name { get; set; } + string Name { get; set; } + string Path { get; set; } TrackedIssue Tracked { get; set; } - public string Tracking { get; set; } + string Tracking { get; set; } } public class KeyOptions : Options, ITrackedOptions @@ -38,7 +37,7 @@ public class KeyOptions : Options, ITrackedOptions string.Empty, ".", "T", - "Tracked" + "Tracked", }; private string key = string.Empty; @@ -48,11 +47,14 @@ public string Key { get { - if (tracking.Contains(key)) - return Tracked?.Key.ToString() ?? IssueKey.None.ToString(); - return key; + if (KeyOptions.tracking.Contains(this.key)) + { + return this.Tracked?.Key.ToString() ?? IssueKey.None.ToString(); + } + + return this.key; } - set => key = value; + set => this.key = value; } /// @@ -63,11 +65,11 @@ public string Key public class EditorOptions : KeyOptions { - [Option("editor", HelpText = "The editor to use", Required = false)] - public string Editor { get; set; } = "joe"; - [Option("arguments", HelpText = "Any additional arguments to give to the editor", Required = false)] public string Arguments { get; set; } = "-pound_comment -syntax git-commit"; + + [Option("editor", HelpText = "The editor to use", Required = false)] + public string Editor { get; set; } = "joe"; } #pragma warning restore 1591 } \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Remove/RemoveCommand.cs b/src/GitIssue.Tool/Commands/Remove/RemoveCommand.cs index 2db178b..0d27cad 100644 --- a/src/GitIssue.Tool/Commands/Remove/RemoveCommand.cs +++ b/src/GitIssue.Tool/Commands/Remove/RemoveCommand.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using GitIssue.Fields; using GitIssue.Fields.Array; +using GitIssue.Issues; using Serilog; namespace GitIssue.Tool.Commands.Remove @@ -25,9 +26,9 @@ public RemoveCommand(IIssueManager manager, ILogger logger) /// public override async Task Exec(RemoveOptions options) { - var formatter = TerminalFormatter.Detailed; + TerminalFormatter formatter = TerminalFormatter.Detailed; - var issue = await manager + IIssue? issue = await this.manager .FindAsync(i => i.Key.ToString() == options.Key) .FirstOrDefaultAsync(); @@ -43,17 +44,17 @@ public override async Task Exec(RemoveOptions options) return; } - var key = FieldKey.Create(options.Field); + FieldKey key = FieldKey.Create(options.Field); if (!issue.ContainsKey(key)) { this.logger.Error($"Field \"{key}\" does not exist on issue \"{issue.Key}\""); return; } - var field = issue[key]; + IField field = issue[key]; if (field is IArrayField arrayField) { - if (arrayField.TryParse(options.Remove, out var value)) + if (arrayField.TryParse(options.Remove, out object? value)) { if (arrayField.Contains(value)) { diff --git a/src/GitIssue.Tool/Commands/Show/ShowCommand.cs b/src/GitIssue.Tool/Commands/Show/ShowCommand.cs index 0ddba0c..32842f0 100644 --- a/src/GitIssue.Tool/Commands/Show/ShowCommand.cs +++ b/src/GitIssue.Tool/Commands/Show/ShowCommand.cs @@ -26,7 +26,7 @@ public ShowCommand(IIssueManager manager, ILogger logger) /// public override async Task Exec(ShowOptions options) { - var formatter = new TerminalFormatter("%*"); + TerminalFormatter formatter = new TerminalFormatter("%*"); IAsyncEnumerable issues; switch (options.Show) @@ -34,19 +34,19 @@ public override async Task Exec(ShowOptions options) case ShowSubCommand.tracked: case ShowSubCommand.Tracked: Console.WriteLine("Showing 'tracked' issue"); - issues = manager.FindAsync(i => i.Key.ToString() == options.Key); + issues = this.manager.FindAsync(i => i.Key.ToString() == options.Key); break; case ShowSubCommand.all: case ShowSubCommand.All: Console.WriteLine("Showing 'all' issues"); - issues = manager.FindAsync(i => true); + issues = this.manager.FindAsync(i => true); break; case ShowSubCommand.mine: case ShowSubCommand.Mine: Console.WriteLine("Showing 'my' issues"); - issues = manager.FindAsync(i => true); + issues = this.manager.FindAsync(i => true); break; default: @@ -55,9 +55,13 @@ public override async Task Exec(ShowOptions options) } int count = 0; - await foreach (var issue in issues) + await foreach (IIssue issue in issues) { - if (count++ > 0) Console.WriteLine(); + if (count++ > 0) + { + Console.WriteLine(); + } + Console.WriteLine(issue.Format(formatter)); } diff --git a/src/GitIssue.Tool/Commands/Show/ShowSubCommand.cs b/src/GitIssue.Tool/Commands/Show/ShowSubCommand.cs index 7d1eeb4..bf8c43e 100644 --- a/src/GitIssue.Tool/Commands/Show/ShowSubCommand.cs +++ b/src/GitIssue.Tool/Commands/Show/ShowSubCommand.cs @@ -9,4 +9,4 @@ public enum ShowSubCommand all, All, } -} +} \ No newline at end of file diff --git a/src/GitIssue.Tool/Commands/Sync/SyncCommand.cs b/src/GitIssue.Tool/Commands/Sync/SyncCommand.cs index e1c44ff..0ebe568 100644 --- a/src/GitIssue.Tool/Commands/Sync/SyncCommand.cs +++ b/src/GitIssue.Tool/Commands/Sync/SyncCommand.cs @@ -23,7 +23,7 @@ public SyncCommand(IIssueManager manager, ILogger logger) /// public override Task Exec(SyncOptions options) { - var importer = new FileImporter(manager); + FileImporter importer = new FileImporter(this.manager); Console.WriteLine($"Exported issues to {options.Import}"); return Task.CompletedTask; } diff --git a/src/GitIssue.Tool/Commands/Track/TrackCommand.cs b/src/GitIssue.Tool/Commands/Track/TrackCommand.cs index e77209a..37da9b8 100644 --- a/src/GitIssue.Tool/Commands/Track/TrackCommand.cs +++ b/src/GitIssue.Tool/Commands/Track/TrackCommand.cs @@ -28,7 +28,7 @@ public override async Task Exec(TrackOptions options) options.Tracked = TrackedIssue.None; if (!string.IsNullOrEmpty(options.Key)) { - var find = await manager.FindAsync(i => i.Key.ToString() == options.Key) + IIssue? find = await this.manager.FindAsync(i => i.Key.ToString() == options.Key) .FirstOrDefaultAsync(); if (find != null) diff --git a/src/GitIssue.Tool/Configuration.cs b/src/GitIssue.Tool/Configuration.cs index 27c640b..83d31ac 100644 --- a/src/GitIssue.Tool/Configuration.cs +++ b/src/GitIssue.Tool/Configuration.cs @@ -8,16 +8,16 @@ namespace GitIssue.Tool public class Configuration : IssueConfiguration { /// - /// Gets or sets the editor to use + /// Gets or sets additional arguments for the editor /// [JsonProperty] - public string Editor { get; set; } = "joe"; + public string Arguments { get; set; } = "-pound_comment -syntax git-commit"; /// - /// Gets or sets additional arguments for the editor + /// Gets or sets the editor to use /// [JsonProperty] - public string Arguments { get; set; } = "-pound_comment -syntax git-commit"; + public string Editor { get; set; } = "joe"; /// /// Reads the configuration from a file @@ -26,7 +26,7 @@ public class Configuration : IssueConfiguration /// the public new static Configuration Read(string file) { - return Read(file); + return IssueConfiguration.Read(file); } } } \ No newline at end of file diff --git a/src/GitIssue.Tool/Editor.cs b/src/GitIssue.Tool/Editor.cs index ae0e5ef..b03c090 100644 --- a/src/GitIssue.Tool/Editor.cs +++ b/src/GitIssue.Tool/Editor.cs @@ -17,13 +17,15 @@ public class Editor : IEditor { private static readonly char CommentChar = '#'; + private static readonly string FieldHeaderRegex = @$"^{Editor.CommentChar}[\s]?([\w]*)[\s]?$"; + private static readonly char Newline = '\n'; private static readonly string FieldTemplate = - $"{Newline}{CommentChar} Please edit the field with your updates. Lines starting" + - $"{Newline}{CommentChar} with '#' will be ignored, leave the file unchanged to abort. "; + $"{Editor.Newline}{Editor.CommentChar} Please edit the field with your updates. Lines starting" + + $"{Editor.Newline}{Editor.CommentChar} with '#' will be ignored, leave the file unchanged to abort. "; + - private static readonly string FieldHeaderRegex = @$"^{CommentChar}[\s]?([\w]*)[\s]?$"; public Editor(Configuration configuration) { @@ -32,9 +34,9 @@ public Editor(Configuration configuration) } /// - /// Gets or sets the successful result + /// Gets or sets the command /// - public int Success { get; set; } = 0; + public string Arguments { get; set; } = string.Empty; /// /// Gets or sets the command @@ -42,49 +44,37 @@ public Editor(Configuration configuration) public string Command { get; set; } = string.Empty; /// - /// Gets or sets the command + /// Gets or sets the successful result /// - public string Arguments { get; set; } = string.Empty; + public int Success { get; set; } = 0; public void UpdateCommand(string command) { - var result = GetProcessAndArgumentsFromCommand(command); + (string, string) result = Editor.GetProcessAndArgumentsFromCommand(command); this.Command = result.Item1; this.Arguments = result.Item2; } - private static (string, string) GetProcessAndArgumentsFromCommand(string command) - { - try - { - var match = Regex.Match(command, "^\\s?([\\w.]+|\"[\\w\\s.]*\")\\s?(.*)?$"); - if (match.Success) - if (match.Groups.Count == 3) - return (match.Groups[1].ToString().Trim(), match.Groups[2].ToString().Trim()); - } - catch (Exception) - { - // Ignored - } - return (command.Trim(), String.Empty); - } - /// public async Task Edit(string header, string content) { - var temp = GetTempFile(); + string temp = Editor.GetTempFile(); - await File.AppendAllTextAsync(temp, $"{CommentChar} {header} {Newline}"); + await File.AppendAllTextAsync(temp, $"{Editor.CommentChar} {header} {Editor.Newline}"); await File.AppendAllTextAsync(temp, content); - await File.AppendAllTextAsync(temp, FieldTemplate); + await File.AppendAllTextAsync(temp, Editor.FieldTemplate); - var created = File.GetLastWriteTime(temp); + DateTime created = File.GetLastWriteTime(temp); - if (await EditFileAsync(this.Command, this.Arguments + " " + temp)) + if (await this.EditFileAsync(this.Command, this.Arguments + " " + temp)) + { if (created == File.GetLastWriteTime(temp)) + { return content; + } + } - return RemoveComments(await File.ReadAllTextAsync(temp)); + return Editor.RemoveComments(await File.ReadAllTextAsync(temp)); } /// @@ -97,21 +87,23 @@ public async Task Open(IIssue issue) public async Task Open(IEnumerable fields) { // Convert fields to a file - var temp = GetTempFile(); - foreach (var field in fields) + string temp = Editor.GetTempFile(); + foreach (IField field in fields) { - await File.AppendAllTextAsync(temp, $"{CommentChar} {field.Key} {Newline}"); - await File.AppendAllTextAsync(temp, $"{await field.ExportAsync()}{Newline}"); + await File.AppendAllTextAsync(temp, $"{Editor.CommentChar} {field.Key} {Editor.Newline}"); + await File.AppendAllTextAsync(temp, $"{await field.ExportAsync()}{Editor.Newline}"); } - await File.AppendAllTextAsync(temp, FieldTemplate); + await File.AppendAllTextAsync(temp, Editor.FieldTemplate); // Open and modify the file - var created = File.GetLastWriteTime(temp); - if (await EditFileAsync(this.Command, this.Arguments + " " + temp)) + DateTime created = File.GetLastWriteTime(temp); + if (await this.EditFileAsync(this.Command, this.Arguments + " " + temp)) { if (created == File.GetLastWriteTime(temp)) + { return; + } } else { @@ -119,11 +111,12 @@ public async Task Open(IEnumerable fields) } // Extract the field updates - var key = FieldKey.None; - var content = string.Empty; - var updates = new Dictionary(); - await foreach (var line in ReadLinesAsync(temp)) - if (IsMatch(line, FieldHeaderRegex, out var match)) + FieldKey key = FieldKey.None; + string content = string.Empty; + Dictionary updates = new Dictionary(); + await foreach (string line in Editor.ReadLinesAsync(temp)) + { + if (Editor.IsMatch(line, Editor.FieldHeaderRegex, out Match match)) { if (key != FieldKey.None) { @@ -137,31 +130,65 @@ public async Task Open(IEnumerable fields) else if (key != FieldKey.None) { if (string.IsNullOrEmpty(content)) + { content = line; + } else - content = content + Newline + line; + { + content = content + Editor.Newline + line; + } } + } // Update the fields - foreach (var field in fields) + foreach (IField field in fields) + { if (updates.ContainsKey(field.Key)) + { field.Update(updates[field.Key]); + } + } } /// public async Task Open(IField field) { - var content = await field.ExportAsync(); - var temp = GetTempFile(); + string content = await field.ExportAsync(); + string temp = Editor.GetTempFile(); await File.WriteAllTextAsync(temp, content); - await File.AppendAllTextAsync(temp, FieldTemplate); + await File.AppendAllTextAsync(temp, Editor.FieldTemplate); - var created = File.GetLastWriteTime(temp); - if (await EditFileAsync(this.Command, this.Arguments + " " + temp)) + DateTime created = File.GetLastWriteTime(temp); + if (await this.EditFileAsync(this.Command, this.Arguments + " " + temp)) + { if (created == File.GetLastWriteTime(temp)) + { return; + } + } + + field.Update(Editor.RemoveComments(await File.ReadAllTextAsync(temp))); + } + + private static (string, string) GetProcessAndArgumentsFromCommand(string command) + { + try + { + Match match = Regex.Match(command, "^\\s?([\\w.]+|\"[\\w\\s.]*\")\\s?(.*)?$"); + if (match.Success) + { + if (match.Groups.Count == 3) + { + return (match.Groups[1].ToString().Trim(), match.Groups[2].ToString().Trim()); + } + } + } + catch (Exception) + { + // Ignored + } - field.Update(RemoveComments(await File.ReadAllTextAsync(temp))); + return (command.Trim(), string.Empty); } private static string GetTempFile() @@ -184,8 +211,8 @@ private static bool IsMatch(string input, string pattern, out Match match) private static async IAsyncEnumerable ReadLinesAsync(string file) { await using Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read); - using var reader = new StreamReader(stream); - var line = await reader.ReadLineAsync(); + using StreamReader reader = new StreamReader(stream); + string? line = await reader.ReadLineAsync(); while (line != null) { yield return line; @@ -195,19 +222,19 @@ private static async IAsyncEnumerable ReadLinesAsync(string file) private static string RemoveComments(string input) { - var comments = $@"^{CommentChar}(.*)$"; - var lines = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None) - .Where(l => Regex.IsMatch(l, comments) == false) + string comments = $@"^{Editor.CommentChar}(.*)$"; + string[] lines = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None) + .Where(l => !Regex.IsMatch(l, comments)) .ToArray(); - return string.Join(Newline, lines); + return string.Join(Editor.Newline, lines); } private async Task EditFileAsync(string editor, string arguments) { - var result = 0; + int result = 0; await Task.Run(() => { - using var process = new Process(); + using Process process = new Process(); process.StartInfo.FileName = editor; process.StartInfo.Arguments = arguments; @@ -222,8 +249,7 @@ await Task.Run(() => result = process.ExitCode; }); - return result == Success; + return result == this.Success; } - } } \ No newline at end of file diff --git a/src/GitIssue.Tool/GitIssue.Tool.csproj b/src/GitIssue.Tool/GitIssue.Tool.csproj index 74b662d..38b0e7f 100644 --- a/src/GitIssue.Tool/GitIssue.Tool.csproj +++ b/src/GitIssue.Tool/GitIssue.Tool.csproj @@ -1,7 +1,7 @@  - net6.0 + net10.0 enable latest @@ -19,18 +19,18 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + + diff --git a/src/GitIssue.Tool/IEditor.cs b/src/GitIssue.Tool/IEditor.cs index 8b6769c..c2d94db 100644 --- a/src/GitIssue.Tool/IEditor.cs +++ b/src/GitIssue.Tool/IEditor.cs @@ -11,7 +11,7 @@ namespace GitIssue.Tool public interface IEditor { /// - /// Edits existing content + /// Edits existing content /// /// the header /// the content diff --git a/src/GitIssue.Tool/Program.cs b/src/GitIssue.Tool/Program.cs index 0e2e87f..8d61451 100644 --- a/src/GitIssue.Tool/Program.cs +++ b/src/GitIssue.Tool/Program.cs @@ -29,50 +29,13 @@ internal class Program { private static ILogger? logger; - private static void Main(string[] args) - { - logger = new LoggerConfiguration() - .WriteTo.Console() - .MinimumLevel.Debug() - .CreateLogger(); - - var parser = new Parser(with => - { - with.EnableDashDash = true; - with.AutoHelp = true; - with.CaseSensitive = false; - with.HelpWriter = Console.Error; - }); - - parser.ParseArguments(args) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()) - .WithParsed(o => ExecAsync(o).Wait()); - } - private static async Task ExecAsync(T options) where TC : Command where T : Options { - var builder = new ContainerBuilder(); + ContainerBuilder builder = new ContainerBuilder(); - builder.Register(c => logger!) + builder.Register(c => Program.logger!) .As() .SingleInstance(); @@ -86,8 +49,8 @@ private static async Task ExecAsync(T options) // Initialize the repository root and save the configuration InitCommand.Initializer onInitCommand = () => { - var config = new IssueConfiguration(); - var root = RepositoryRoot.Create(options.Path, options.Name); + IssueConfiguration config = new IssueConfiguration(); + RepositoryRoot root = RepositoryRoot.Create(options.Path, options.Name); config.Save(root.ConfigFile); }; return onInitCommand; @@ -107,32 +70,68 @@ private static async Task ExecAsync(T options) builder.RegisterModule(); - using var container = builder.Build(); + using IContainer container = builder.Build(); if (options is ITrackedOptions keyOptions) + { keyOptions.Tracked = TrackedIssue - .Read(Path.Combine(options.Path, options.Name, options.Tracking), logger); + .Read(Path.Combine(options.Path, options.Name, options.Tracking), Program.logger); + } - var command = container.Resolve>(); - await ExecAsync(command.Exec, options); + Command command = container.Resolve>(); + await Program.ExecAsync(command.Exec, options); } private static async Task ExecAsync(Func func, T value) where T : Options { - await Task.Run(async () => + try { - try - { - Console.WriteLine(); - await func(value); - Console.WriteLine(); - } - catch (Exception e) - { - logger?.Error($"Exception caught when executing command: {e.Message}", e); - } + Console.WriteLine(); + await func(value); + Console.WriteLine(); + } + catch (Exception e) + { + Program.logger?.Error($"Exception caught when executing command: {e.Message}", e); + } + } + + private static void Main(string[] args) + { + Program.logger = new LoggerConfiguration() + .WriteTo.Console() + .MinimumLevel.Debug() + .CreateLogger(); + + Parser parser = new Parser(with => + { + with.EnableDashDash = true; + with.AutoHelp = true; + with.CaseSensitive = false; + with.HelpWriter = Console.Error; }); + + parser.ParseArguments(args) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()) + .WithParsed(o => Program.ExecAsync(o).Wait()); } } } \ No newline at end of file diff --git a/src/GitIssue.Tool/Properties/PublishProfiles/Win10-x64_Profile.pubxml b/src/GitIssue.Tool/Properties/PublishProfiles/Win10-x64_Profile.pubxml deleted file mode 100644 index 524cdc9..0000000 --- a/src/GitIssue.Tool/Properties/PublishProfiles/Win10-x64_Profile.pubxml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - FileSystem - Release - netcoreapp3.1 - ../output/GitIssue.Tool/bin/Release/netcoreapp3.1/win10-x64/publish/ - win10-x64 - Any CPU - true - True - True - False - - \ No newline at end of file diff --git a/src/GitIssue.Tool/TerminalFormatter.cs b/src/GitIssue.Tool/TerminalFormatter.cs index 3bb5282..e295d2c 100644 --- a/src/GitIssue.Tool/TerminalFormatter.cs +++ b/src/GitIssue.Tool/TerminalFormatter.cs @@ -1,5 +1,4 @@ - -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; @@ -8,31 +7,29 @@ using GitIssue.Issues; using Humanizer; using Pastel; +using DateTime = GitIssue.Values.DateTime; namespace GitIssue.Tool { public class TerminalFormatter : IssueFormatter { - private static Dictionary> terminalIssueFormatters = new Dictionary> - { - {"*", (t, i) => i.Select(f => $"{f.Key.ToString().PadRight(20,' ').Pastel(Color.FromArgb(102, 255, 102))} {t.Format(f.Value)}") - .Aggregate((a, b) => $"{a}{Environment.NewLine}{b}") }, - - { "Delta", (t, i) => $"({i.GetField("Updated").AsValue().Item.Humanize()})".Pastel(Color.FromArgb(255, 153, 0)) } - }; + private static readonly Dictionary> terminalFieldFormatters = new Dictionary> { { "Key", (t, f) => $"{f.ToString()?.PadRight(20, ' ').Pastel(Color.FromArgb(165, 229, 250))}" } }; - private static Dictionary> terminalFieldFormatters = new Dictionary> + private static readonly Dictionary> terminalIssueFormatters = new Dictionary> { - {"Key", (t, f) => $"{f.ToString()?.PadRight(20,' ').Pastel(Color.FromArgb(165, 229, 250))}"}, + { + "*", (t, i) => i.Select(f => $"{f.Key.ToString().PadRight(20, ' ').Pastel(Color.FromArgb(102, 255, 102))} {t.Format(f.Value)}") + .Aggregate((a, b) => $"{a}{Environment.NewLine}{b}") + }, + { "Delta", (t, i) => $"({i.GetField("Updated").AsValue().Item.Humanize()})".Pastel(Color.FromArgb(255, 153, 0)) }, }; - public TerminalFormatter(string format) : base(format, terminalIssueFormatters, terminalFieldFormatters) + public TerminalFormatter(string format) : base(format, TerminalFormatter.terminalIssueFormatters, TerminalFormatter.terminalFieldFormatters) { - } - public new static TerminalFormatter Simple => new TerminalFormatter("%Key %Title %Delta"); - public new static TerminalFormatter Detailed => new TerminalFormatter("%*"); + + public new static TerminalFormatter Simple => new TerminalFormatter("%Key %Title %Delta"); } -} +} \ No newline at end of file diff --git a/src/GitIssue.sln.DotSettings b/src/GitIssue.sln.DotSettings index ca6984b..5413fa4 100644 --- a/src/GitIssue.sln.DotSettings +++ b/src/GitIssue.sln.DotSettings @@ -1,3 +1,385 @@  + Inherit - <?xml version="1.0" encoding="utf-16"?><Profile name="GitIssue: Full Cleanup"><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><CSReorderTypeMembers>True</CSReorderTypeMembers><CSCodeStyleAttributes ArrangeVarStyle="True" ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" ArrangeArgumentsStyle="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeCodeBodyStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" /><CssAlphabetizeProperties>True</CssAlphabetizeProperties><JSStringLiteralQuotesDescriptor>True</JSStringLiteralQuotesDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><JsInsertSemicolon>True</JsInsertSemicolon><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs><OptimizeReferenceCommentsTs>True</OptimizeReferenceCommentsTs><PublicModifierStyleTs>True</PublicModifierStyleTs><ExplicitAnyTs>True</ExplicitAnyTs><TypeAnnotationStyleTs>True</TypeAnnotationStyleTs><RelativePathStyleTs>True</RelativePathStyleTs><AsInsteadOfCastTs>True</AsInsteadOfCastTs><RemoveCodeRedundanciesVB>True</RemoveCodeRedundanciesVB><Xaml.RedundantFreezeAttribute>True</Xaml.RedundantFreezeAttribute><Xaml.RemoveRedundantModifiersAttribute>True</Xaml.RemoveRedundantModifiersAttribute><Xaml.RemoveRedundantNameAttribute>True</Xaml.RemoveRedundantNameAttribute><Xaml.RemoveRedundantResource>True</Xaml.RemoveRedundantResource><Xaml.RemoveRedundantCollectionProperty>True</Xaml.RemoveRedundantCollectionProperty><Xaml.RemoveRedundantAttachedPropertySetter>True</Xaml.RemoveRedundantAttachedPropertySetter><Xaml.RemoveRedundantStyledValue>True</Xaml.RemoveRedundantStyledValue><Xaml.RemoveRedundantNamespaceAlias>True</Xaml.RemoveRedundantNamespaceAlias><Xaml.RemoveForbiddenResourceName>True</Xaml.RemoveForbiddenResourceName><Xaml.RemoveRedundantGridDefinitionsAttribute>True</Xaml.RemoveRedundantGridDefinitionsAttribute><Xaml.RemoveRedundantUpdateSourceTriggerAttribute>True</Xaml.RemoveRedundantUpdateSourceTriggerAttribute><Xaml.RemoveRedundantBindingModeAttribute>True</Xaml.RemoveRedundantBindingModeAttribute><Xaml.RemoveRedundantGridSpanAttribut>True</Xaml.RemoveRedundantGridSpanAttribut><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CssReformatCode>True</CssReformatCode><HtmlReformatCode>True</HtmlReformatCode><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><VBOptimizeImports>True</VBOptimizeImports><VBShortenReferences>True</VBShortenReferences><XMLReformatCode>True</XMLReformatCode><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><VBReformatCode>True</VBReformatCode><VBFormatDocComments>True</VBFormatDocComments><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor></Profile> \ No newline at end of file + + True + True + PerTechnology + False + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + WARNING + SUGGESTION + SUGGESTION + WARNING + WARNING + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + SUGGESTION + + DO_NOT_SHOW + SUGGESTION + SUGGESTION + WARNING + SUGGESTION + False + ECMAScript 2016 + <?xml version="1.0" encoding="utf-16"?><Profile name="GitIssue: Full Cleanup"><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><CppAddTypenameTemplateKeywords>True</CppAddTypenameTemplateKeywords><CppCStyleToStaticCastDescriptor>True</CppCStyleToStaticCastDescriptor><CppRedundantDereferences>True</CppRedundantDereferences><CppDeleteRedundantAccessSpecifier>True</CppDeleteRedundantAccessSpecifier><CppRemoveCastDescriptor>True</CppRemoveCastDescriptor><CppRemoveElseKeyword>True</CppRemoveElseKeyword><CppShortenQualifiedName>True</CppShortenQualifiedName><CppDeleteRedundantSpecifier>True</CppDeleteRedundantSpecifier><CppRemoveStatement>True</CppRemoveStatement><CppDeleteRedundantTypenameTemplateKeywords>True</CppDeleteRedundantTypenameTemplateKeywords><CppReplaceExpressionWithBooleanConst>True</CppReplaceExpressionWithBooleanConst><CppMakeIfConstexpr>True</CppMakeIfConstexpr><CppMakePostfixOperatorPrefix>True</CppMakePostfixOperatorPrefix><CppMakeVariableConstexpr>True</CppMakeVariableConstexpr><CppChangeSmartPointerToMakeFunction>True</CppChangeSmartPointerToMakeFunction><CppReplaceThrowWithRethrowFix>True</CppReplaceThrowWithRethrowFix><CppTypeTraitAliasDescriptor>True</CppTypeTraitAliasDescriptor><CppRemoveRedundantConditionalExpressionDescriptor>True</CppRemoveRedundantConditionalExpressionDescriptor><CppSimplifyConditionalExpressionDescriptor>True</CppSimplifyConditionalExpressionDescriptor><CppReplaceExpressionWithNullptr>True</CppReplaceExpressionWithNullptr><CppReplaceTieWithStructuredBindingDescriptor>True</CppReplaceTieWithStructuredBindingDescriptor><CppUseAssociativeContainsDescriptor>True</CppUseAssociativeContainsDescriptor><CppUseEraseAlgorithmDescriptor>True</CppUseEraseAlgorithmDescriptor><CppCodeStyleCleanupDescriptor ArrangeBraces="True" ArrangeAuto="True" ArrangeFunctionDeclarations="True" ArrangeNestedNamespaces="True" ArrangeTypeAliases="True" ArrangeCVQualifiers="True" ArrangeSlashesInIncludeDirectives="True" ArrangeOverridingFunctions="True" SortDefinitions="True" SortIncludeDirectives="True" SortMemberInitializers="True" /><CppReformatCode>True</CppReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><CSCodeStyleAttributes ArrangeVarStyle="True" ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" ArrangeArgumentsStyle="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeCodeBodyStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" ArrangeNullCheckingPattern="True" /><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><ShaderLabReformatCode>True</ShaderLabReformatCode><StandaloneSqlReformatCode>True</StandaloneSqlReformatCode><InjectedSqlReformatCode>True</InjectedSqlReformatCode><RemoveCodeRedundanciesVB>True</RemoveCodeRedundanciesVB><VBMakeFieldReadonly>True</VBMakeFieldReadonly><Xaml.RemoveRedundantNamespaceAlias>True</Xaml.RemoveRedundantNamespaceAlias><Xaml.RedundantFreezeAttribute>True</Xaml.RedundantFreezeAttribute><Xaml.RemoveRedundantModifiersAttribute>True</Xaml.RemoveRedundantModifiersAttribute><Xaml.RemoveRedundantNameAttribute>True</Xaml.RemoveRedundantNameAttribute><Xaml.RemoveRedundantResource>True</Xaml.RemoveRedundantResource><Xaml.RemoveRedundantCollectionProperty>True</Xaml.RemoveRedundantCollectionProperty><Xaml.RemoveRedundantAttachedPropertySetter>True</Xaml.RemoveRedundantAttachedPropertySetter><Xaml.RemoveRedundantStyledValue>True</Xaml.RemoveRedundantStyledValue><Xaml.RemoveForbiddenResourceName>True</Xaml.RemoveForbiddenResourceName><Xaml.RemoveRedundantGridDefinitionsAttribute>True</Xaml.RemoveRedundantGridDefinitionsAttribute><Xaml.RemoveRedundantUpdateSourceTriggerAttribute>True</Xaml.RemoveRedundantUpdateSourceTriggerAttribute><Xaml.RemoveRedundantBindingModeAttribute>True</Xaml.RemoveRedundantBindingModeAttribute><Xaml.RemoveRedundantGridSpanAttribut>True</Xaml.RemoveRedundantGridSpanAttribut><XMLReformatCode>True</XMLReformatCode><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><HtmlReformatCode>True</HtmlReformatCode><VBOptimizeImports>True</VBOptimizeImports><VBShortenReferences>True</VBShortenReferences><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><VBReformatCode>True</VBReformatCode><VBFormatDocComments>True</VBFormatDocComments><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSharpReformatComments>True</CSharpReformatComments><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor></Profile> + + Required + Required + Required + Required + Required + DefaultLiteral + DefaultExpression + Separate + BlockScoped + ExplicitlyTyped + Shift, Relational, Equality, Bitwise, Conditional + Field, Property, Event, Method + Field, Property, Event, Method + True + False + False + False + False + False + False + False + False + True + False + False + False + True + False + False + False + False + False + 3 + 3 + True + NEVER + IF_OWNER_IS_SINGLE_LINE + True + NEVER + True + DO_NOT_CHANGE + False + True + CHOP_IF_LONG + + 320 + True + + True + 1 + 80 + ByFirstAttr + 160 + OneStep + OneStep + OnDifferentLines + False + 320 + <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> + <TypePattern DisplayName="Non-reorderable types"> + <TypePattern.Match> + <Or> + <And> + <Kind Is="Interface" /> + <Or> + <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> + <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> + </Or> + </And> + <Kind Is="Struct" /> + <HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" /> + <HasAttribute Name="JetBrains.Annotations.NoReorder" /> + </Or> + </TypePattern.Match> + </TypePattern> + <TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All"> + <TypePattern.Match> + <And> + <Kind Is="Class" /> + <HasMember> + <And> + <Kind Is="Method" /> + <HasAttribute Inherited="True" Name="Xunit.FactAttribute" /> + </And> + </HasMember> + </And> + </TypePattern.Match> + <Entry DisplayName="Setup/Teardown Methods"> + <Entry.Match> + <Or> + <Kind Is="Constructor" /> + <And> + <Kind Is="Method" /> + <ImplementsInterface Name="System.IDisposable" /> + </And> + </Or> + </Entry.Match> + <Entry.SortBy> + <Kind Is="0" Order="Constructor" /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="All other members" /> + <Entry DisplayName="Test Methods" Priority="100"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <HasAttribute Name="Xunit.FactAttribute" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + </TypePattern> + <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> + <TypePattern.Match> + <And> + <Kind Is="Class" /> + <HasAttribute Inherited="True" Name="NUnit.Framework.TestFixtureAttribute" /> + </And> + </TypePattern.Match> + <Entry DisplayName="Setup/Teardown Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Or> + <HasAttribute Inherited="True" Name="NUnit.Framework.SetUpAttribute" /> + <HasAttribute Inherited="True" Name="NUnit.Framework.TearDownAttribute" /> + <HasAttribute Inherited="True" Name="NUnit.Framework.FixtureSetUpAttribute" /> + <HasAttribute Inherited="True" Name="NUnit.Framework.FixtureTearDownAttribute" /> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="All other members" /> + <Entry DisplayName="Test Methods" Priority="100"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <HasAttribute Name="NUnit.Framework.TestAttribute" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + </TypePattern> + <TypePattern DisplayName="Default Pattern"> + <Entry DisplayName="Enums"> + <Entry.Match> + <Kind Is="Enum" /> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Constants"> + <Entry.Match> + <Kind Is="Constant" /> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Public Delegates"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Delegate" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Fields"> + <Entry.Match> + <Kind Is="Field" /> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Readonly /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Constructors" Priority="100"> + <Entry.Match> + <Kind Is="Constructor" /> + </Entry.Match> + </Entry> + <Entry DisplayName="IDisposable" Priority="100"> + <Entry.Match> + <And> + <ImplementsInterface Name="IDisposable" /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Properties, Indexers"> + <Entry.Match> + <Or> + <Kind Is="Property" /> + <Kind Is="Autoproperty" /> + <Kind Is="Indexer" /> + </Or> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Access Is="0" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Public Methods" Priority="100"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Or> + <Access Is="Public" /> + <Access Is="Internal" /> + </Or> + </And> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Access Is="0" /> + <ImplementsInterface /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Protected Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Or> + <Access Is="Protected" /> + <Access Is="ProtectedInternal" /> + </Or> + </And> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Access Is="0" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Private Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Access Is="Private" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Access Is="0" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="All other members" Priority="25"> + <Entry.SortBy> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Nested Types"> + <Entry.Match> + <Kind Is="Type" /> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + </TypePattern> +</Patterns> + UseExplicitType + UseExplicitType + UseExplicitType + False + True + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Private static"><ElementKinds><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + + True + + True + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Private methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /><Kind Name="CLASS" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True + True + True + True + True + + \ No newline at end of file diff --git a/src/GitIssue/ChangeLog.cs b/src/GitIssue/ChangeLog.cs index a2f0ee5..55bdfa3 100644 --- a/src/GitIssue/ChangeLog.cs +++ b/src/GitIssue/ChangeLog.cs @@ -19,47 +19,67 @@ public class ChangeLog : IChangeLog /// public Dictionary> Log { get; set; } = new Dictionary>(); - /// - public void Clear() + /// + /// Reads the configuration from a file + /// + /// the configuration file + /// the + public static ChangeLog Read(string file) { - hasChanged = true; - Log.Clear(); + if (!File.Exists(file)) + { + return new ChangeLog(); + } + + try + { + using FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read); + using StreamReader reader = new StreamReader(stream); + JsonSerializer serializer = new JsonSerializer(); + ChangeLog? configuration = serializer.Deserialize(reader, typeof(ChangeLog)) as ChangeLog; + return configuration ?? new ChangeLog(); + } + catch (Exception ex) + { + throw new AggregateException($"Unable to deserialize {file} as change log ", ex); + } } /// public void Add(IssueKey key, ChangeType change) { - Add(key, change, string.Empty); + this.Add(key, change, string.Empty); } /// public void Add(IssueKey key, ChangeType change, string summary) { - hasChanged = true; - if (Log.ContainsKey(key) == false) - Log[key] = new List(); + this.hasChanged = true; + if (!this.Log.ContainsKey(key)) + { + this.Log[key] = new List(); + } - Log[key].Add($"{DateTime.Now}: {GetChangeDescription(change)}"); + this.Log[key].Add($"{DateTime.Now}: {ChangeLog.GetChangeDescription(change)}"); } /// public void Add(IIssue issue, ChangeType change) { - Add(issue.Key, change, string.Empty); + this.Add(issue.Key, change, string.Empty); } /// public void Add(IIssue issue, ChangeType change, string summary) { - Add(issue.Key, change, string.Empty); + this.Add(issue.Key, change, string.Empty); } - private static string GetChangeDescription(ChangeType change) + /// + public void Clear() { - var attribute = typeof(ChangeType) - .GetField(change.ToString()) - ?.GetCustomAttribute(); - return attribute != null ? attribute.Description : change.ToString(); + this.hasChanged = true; + this.Log.Clear(); } /// @@ -68,18 +88,16 @@ private static string GetChangeDescription(ChangeType change) /// the configuration file public void Save(string file) { - if (hasChanged == false) + if (!this.hasChanged) + { return; + } try { - using var stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); - using var writer = new StreamWriter(stream); - var serializer = JsonSerializer.Create(new JsonSerializerSettings - { - Formatting = Formatting.Indented, - DefaultValueHandling = DefaultValueHandling.Ignore - }); + using FileStream stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); + using StreamWriter writer = new StreamWriter(stream); + JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings { Formatting = Formatting.Indented, DefaultValueHandling = DefaultValueHandling.Ignore }); serializer.Serialize(writer, this, typeof(ChangeLog)); } catch (Exception ex) @@ -88,28 +106,12 @@ public void Save(string file) } } - /// - /// Reads the configuration from a file - /// - /// the configuration file - /// the - public static ChangeLog Read(string file) + private static string GetChangeDescription(ChangeType change) { - if (File.Exists(file) == false) - return new ChangeLog(); - - try - { - using var stream = new FileStream(file, FileMode.Open, FileAccess.Read); - using var reader = new StreamReader(stream); - var serializer = new JsonSerializer(); - var configuration = serializer.Deserialize(reader, typeof(ChangeLog)) as ChangeLog; - return configuration ?? new ChangeLog(); - } - catch (Exception ex) - { - throw new AggregateException($"Unable to deserialize {file} as change log ", ex); - } + DescriptionAttribute? attribute = typeof(ChangeType) + .GetField(change.ToString()) + ?.GetCustomAttribute(); + return attribute != null ? attribute.Description : change.ToString(); } } } \ No newline at end of file diff --git a/src/GitIssue/ChangeLogExtensions.cs b/src/GitIssue/ChangeLogExtensions.cs index 16e4b19..68d02f4 100644 --- a/src/GitIssue/ChangeLogExtensions.cs +++ b/src/GitIssue/ChangeLogExtensions.cs @@ -1,9 +1,11 @@ -using System.Text; +using System.Collections.Generic; +using System.Text; +using GitIssue.Issues; namespace GitIssue { /// - /// Extension methods for the change log + /// Extension methods for the change log /// public static class ChangeLogExtensions { @@ -14,15 +16,23 @@ public static class ChangeLogExtensions /// public static string GenerateComments(this IChangeLog log) { - var builder = new StringBuilder(); - var count = 0; - foreach (var changes in log.Log) + StringBuilder builder = new StringBuilder(); + int count = 0; + foreach (KeyValuePair> changes in log.Log) { - if (count++ > 0) builder.AppendLine(); + if (count++ > 0) + { + builder.AppendLine(); + } + builder.AppendLine($"Issue: {changes.Key}"); - foreach (var change in changes.Value) builder.AppendLine($" - {change}"); + foreach (string change in changes.Value) + { + builder.AppendLine($" - {change}"); + } } + return builder.ToString(); } } -} +} \ No newline at end of file diff --git a/src/GitIssue/ChangeType.cs b/src/GitIssue/ChangeType.cs index 7c2a068..6c3528a 100644 --- a/src/GitIssue/ChangeType.cs +++ b/src/GitIssue/ChangeType.cs @@ -7,7 +7,8 @@ public enum ChangeType { [Description("Created new issue")] Create, - [Description("Deleted existing issue")] Delete + [Description("Deleted existing issue")] + Delete, } #pragma warning restore 1591 } \ No newline at end of file diff --git a/src/GitIssue/Fields/Array/ArrayField.cs b/src/GitIssue/Fields/Array/ArrayField.cs index 38e0ff4..9de110e 100644 --- a/src/GitIssue/Fields/Array/ArrayField.cs +++ b/src/GitIssue/Fields/Array/ArrayField.cs @@ -26,20 +26,62 @@ protected ArrayField(FieldKey key, T[] values) : base(key) this.values = new List(values); } + /// + public int Count => this.values.Count; + + /// + public T this[int index] + { + get => this.values[index]; + set => this.values[index] = value; + } + + /// + public T[] Values + { + get => this.values.ToArray(); + set => this.values = new List(value); + } + /// public Type ValueType => typeof(T); + /// + int ICollection.Count => ((ICollection)this.values).Count; + + bool IList.IsFixedSize => ((IList)this.values).IsFixedSize; + + /// + bool IList.IsReadOnly => ((IList)this.values).IsReadOnly; + + bool ICollection.IsReadOnly => ((ICollection)this.values).IsReadOnly; + + /// + bool ICollection.IsSynchronized => ((ICollection)this.values).IsSynchronized; + + /// + object? IList.this[int index] + { + get => ((IList)this.values)[index]; + set => ((IList)this.values)[index] = value; + } + + /// + object ICollection.SyncRoot => ((ICollection)this.values).SyncRoot; + /// object[]? IArrayField.Values { - get => Values.Cast().ToArray(); + get => this.Values.Cast().ToArray(); set { if (value == null) + { return; + } this.values.Clear(); - foreach (var v in value) + foreach (object v in value) { if (v is T result) { @@ -50,55 +92,89 @@ protected ArrayField(FieldKey key, T[] values) : base(key) } /// - public T[] Values + public override string ToString() { - get => values.ToArray(); - set => values = new List(value); - } + StringBuilder builder = new StringBuilder(); + builder.Append("["); + for (int i = 0; i < this.values.Count; i++) + { + if (i > 0) + { + builder.Append(", "); + } - bool IList.IsFixedSize => ((IList)values).IsFixedSize; + builder.Append(this.Values[i]); + } - /// - bool IList.IsReadOnly => ((IList)values).IsReadOnly; + builder.Append("]"); + return builder.ToString(); + } /// - int ICollection.Count => ((ICollection)values).Count; + public bool TryParse(string input, out T value) + { + return ValueExtensions.TryParse(input, out value); + } /// - bool ICollection.IsSynchronized => ((ICollection)values).IsSynchronized; + public void Add(T item) + { + this.values.Add(item); + } /// - object ICollection.SyncRoot => ((ICollection)values).SyncRoot; + public void Clear() + { + this.values.Clear(); + } /// - public int Count => values.Count; - - bool ICollection.IsReadOnly => ((ICollection)values).IsReadOnly; + public bool Contains(T item) + { + return this.values.Contains(item); + } /// - public T this[int index] + public void CopyTo(T[] array, int arrayIndex) { - get => values[index]; - set => values[index] = value; + this.values.CopyTo(array, arrayIndex); } /// - object? IList.this[int index] + public bool Remove(T item) { - get => ((IList)values)[index]; - set => ((IList)values)[index] = value; + return this.values.Remove(item); } /// public IEnumerator GetEnumerator() { - foreach (var value in Values) yield return value; + foreach (T value in this.Values) + { + yield return value; + } } /// - IEnumerator IEnumerable.GetEnumerator() + public override bool Equals([AllowNull] IField other) { - return GetEnumerator(); + if (other is IArrayField valueField) + { + return this.Values?.SequenceEqual(valueField.Values) ?? false; + } + + return false; + } + + /// + public override bool Copy([AllowNull] IField other) + { + if (other is IArrayField valueField) + { + this.Values = valueField.Values.ToArray(); + } + + return false; } /// @@ -107,28 +183,45 @@ public override bool Update(string input) T result; if (input.StartsWith('+')) { - if (TryParse(input.TrimStart('+'), out result)) - if (!values.Contains(result)) - values.Add(result); + if (this.TryParse(input.TrimStart('+'), out result)) + { + if (!this.values.Contains(result)) + { + this.values.Add(result); + } + } + return true; } if (input.StartsWith('-')) { - if (TryParse(input.TrimStart('-'), out result)) - if (values.Contains(result)) - values.Remove(result); + if (this.TryParse(input.TrimStart('-'), out result)) + { + if (this.values.Contains(result)) + { + this.values.Remove(result); + } + } + return true; } if (input.StartsWith('[') && input.EndsWith(']')) { this.values.Clear(); - var values = input.TrimStart('[').TrimEnd(']').Split(','); - foreach (var value in values) - if (TryParse(value.Trim(), out result)) + string[] values = input.TrimStart('[').TrimEnd(']').Split(','); + foreach (string value in values) + { + if (this.TryParse(value.Trim(), out result)) + { if (!this.values.Contains(result)) + { this.values.Add(result); + } + } + } + return true; } @@ -136,97 +229,73 @@ public override bool Update(string input) } /// - void IList.RemoveAt(int index) + public int IndexOf(T item) { - values.RemoveAt(index); + return this.values.IndexOf(item); } /// - int IList.Add(object? value) + public void Insert(int index, T item) { - return ((IList)values).Add(value); + this.values.Insert(index, item); } /// - void IList.Clear() + public void RemoveAt(int index) { - values.Clear(); + this.values.RemoveAt(index); } /// - bool IList.Contains(object? value) + int IList.Add(object? value) { - return ((IList)values).Contains(value); + return ((IList)this.values).Add(value); } /// - int IList.IndexOf(object? value) + void IList.Clear() { - return ((IList)values).IndexOf(value); + this.values.Clear(); } /// - void IList.Insert(int index, object? value) - { - ((IList)values).Insert(index, value); - } - - void IList.Remove(object? value) + bool IList.Contains(object? value) { - ((IList)values).Remove(value); + return ((IList)this.values).Contains(value); } void ICollection.CopyTo(System.Array array, int index) { - ((IList)values).CopyTo(array, index); - } - - /// - public int IndexOf(T item) - { - return values.IndexOf(item); - } - - /// - public void Insert(int index, T item) - { - values.Insert(index, item); + ((IList)this.values).CopyTo(array, index); } /// - public void RemoveAt(int index) - { - values.RemoveAt(index); - } - - /// - public void Add(T item) + IEnumerator IEnumerable.GetEnumerator() { - values.Add(item); + return this.GetEnumerator(); } /// - public bool Contains(T item) + int IList.IndexOf(object? value) { - return values.Contains(item); + return ((IList)this.values).IndexOf(value); } /// - public void CopyTo(T[] array, int arrayIndex) + void IList.Insert(int index, object? value) { - values.CopyTo(array, arrayIndex); + ((IList)this.values).Insert(index, value); } - /// - public bool Remove(T item) + void IList.Remove(object? value) { - return values.Remove(item); + ((IList)this.values).Remove(value); } /// - public void Clear() + void IList.RemoveAt(int index) { - values.Clear(); + this.values.RemoveAt(index); } /// @@ -241,45 +310,5 @@ bool IArrayField.TryParse(string input, out object? value) value = null; return false; } - - /// - public bool TryParse(string input, out T value) - { - return ValueExtensions.TryParse(input, out value); - } - - /// - public override string ToString() - { - var builder = new StringBuilder(); - builder.Append("["); - for (var i = 0; i < values.Count; i++) - { - if (i > 0) builder.Append(", "); - builder.Append(Values[i]); - } - builder.Append("]"); - return builder.ToString(); - } - - /// - public override bool Copy([AllowNull] IField other) - { - if (other is IArrayField valueField) - { - this.Values = valueField.Values.ToArray(); - } - return false; - } - - /// - public override bool Equals([AllowNull] IField other) - { - if (other is IArrayField valueField) - { - return this.Values?.SequenceEqual(valueField.Values) ?? false; - } - return false; - } } } \ No newline at end of file diff --git a/src/GitIssue/Fields/Array/IArrayField.cs b/src/GitIssue/Fields/Array/IArrayField.cs index 1fc3eda..a9ecf3e 100644 --- a/src/GitIssue/Fields/Array/IArrayField.cs +++ b/src/GitIssue/Fields/Array/IArrayField.cs @@ -10,14 +10,14 @@ namespace GitIssue.Fields.Array public interface IArrayField : IField, IList { /// - /// Gets the value type + /// Gets or sets the value /// - Type ValueType { get; } + object[]? Values { get; set; } /// - /// Gets or sets the value + /// Gets the value type /// - object[]? Values { get; set; } + Type ValueType { get; } /// /// Tries to parse the input into an array value diff --git a/src/GitIssue/Fields/EmptyField.cs b/src/GitIssue/Fields/EmptyField.cs index 2dde83f..37158da 100644 --- a/src/GitIssue/Fields/EmptyField.cs +++ b/src/GitIssue/Fields/EmptyField.cs @@ -4,31 +4,46 @@ namespace GitIssue.Fields { /// - /// Implementation of an empty field + /// Implementation of an empty field /// public class EmptyField : Field { /// - /// Initializes a new instance of an empty field + /// Initializes a new instance of an empty field /// /// public EmptyField(FieldKey key) : base(key) { } - /// - public override bool Copy([AllowNull] IField other) => true; + /// + public override bool Copy([AllowNull] IField other) + { + return true; + } - /// - public override bool Equals([AllowNull] IField other) => other is EmptyField; + /// + public override bool Equals([AllowNull] IField other) + { + return other is EmptyField; + } - /// - public override Task ExportAsync() => Task.FromResult(string.Empty); + /// + public override Task ExportAsync() + { + return Task.FromResult(string.Empty); + } - /// - public override Task SaveAsync() => Task.FromResult(false); + /// + public override Task SaveAsync() + { + return Task.FromResult(false); + } - /// - public override bool Update(string input) => false; + /// + public override bool Update(string input) + { + return false; + } } -} +} \ No newline at end of file diff --git a/src/GitIssue/Fields/Field.cs b/src/GitIssue/Fields/Field.cs index c0ee9fd..baeec9f 100644 --- a/src/GitIssue/Fields/Field.cs +++ b/src/GitIssue/Fields/Field.cs @@ -14,7 +14,7 @@ public abstract class Field : IField /// the field key protected Field(FieldKey key) { - Key = key; + this.Key = key; } /// @@ -23,18 +23,18 @@ protected Field(FieldKey key) public FieldKey Key { get; protected set; } /// - public abstract bool Update(string input); + public abstract bool Equals([AllowNull] IField other); /// - public abstract Task SaveAsync(); + public abstract bool Copy([AllowNull] IField other); /// - public abstract Task ExportAsync(); + public abstract Task SaveAsync(); /// - public abstract bool Copy([AllowNull] IField other); + public abstract bool Update(string input); /// - public abstract bool Equals([AllowNull] IField other); + public abstract Task ExportAsync(); } } \ No newline at end of file diff --git a/src/GitIssue/Fields/FieldFactory.cs b/src/GitIssue/Fields/FieldFactory.cs index 85ff084..4637dff 100644 --- a/src/GitIssue/Fields/FieldFactory.cs +++ b/src/GitIssue/Fields/FieldFactory.cs @@ -28,30 +28,33 @@ public FieldFactory(Issue issue, FieldKey key, Func callback) this.key = key; } - /// - public void WithValue(T value) where T : IValue - { - var field = callback?.Invoke(); - if (field is ValueField valueField) - valueField.Value = value; - } - /// public void WithArray(T[] values) where T : IValue { - var field = callback?.Invoke(); - if (field is ArrayField arrayField) arrayField.Values = values; + IField? field = this.callback?.Invoke(); + if (field is ArrayField arrayField) + { + arrayField.Values = values; + } } /// public void WithField(IField updated) { - var field = callback?.Invoke(); + IField? field = this.callback?.Invoke(); if (field?.GetType() == updated.GetType()) { - } + } + /// + public void WithValue(T value) where T : IValue + { + IField? field = this.callback?.Invoke(); + if (field is ValueField valueField) + { + valueField.Value = value; + } } } } \ No newline at end of file diff --git a/src/GitIssue/Fields/FieldInfo.cs b/src/GitIssue/Fields/FieldInfo.cs index 49370a4..ffe332a 100644 --- a/src/GitIssue/Fields/FieldInfo.cs +++ b/src/GitIssue/Fields/FieldInfo.cs @@ -31,35 +31,15 @@ public FieldInfo() : this(false) /// public FieldInfo(bool required) { - Required = required; + this.Required = required; } /// - /// Gets the list of field readers - /// - [JsonIgnore] - public IFieldReader[] Readers - { - get - { - return readers ??= GetType().Assembly.GetTypes() - .Where(t => t.IsPublic) - .Where(t => t.IsGenericType == false) - .Where(t => t.IsAbstract == false) - .Where(t => typeof(IFieldReader).IsAssignableFrom(t)) - .Where(t => t.GetConstructor(Type.EmptyTypes) != null) - .Select(Activator.CreateInstance) - .Cast() - .ToArray(); - } - } - - /// - /// Gets or sets if the field is required on a given issue + /// Gets or sets the metadata for the field /// [JsonProperty] - [DefaultValue(false)] - public bool Required { get; set; } + [DefaultValue("")] + public virtual string FieldMetadata { get; set; } = string.Empty; /// /// Gets or sets the data type for the field @@ -68,17 +48,26 @@ public IFieldReader[] Readers public virtual TypeValue FieldType { get; set; } = TypeValue.Create(); /// - /// Gets or sets the metadata for the field + /// Gets the list of field readers /// - [JsonProperty] - [DefaultValue("")] - public virtual string FieldMetadata { get; set; } = string.Empty; + [JsonIgnore] + public IFieldReader[] Readers => + FieldInfo.readers ??= this.GetType().Assembly.GetTypes() + .Where(t => t.IsPublic) + .Where(t => !t.IsGenericType) + .Where(t => !t.IsAbstract) + .Where(t => typeof(IFieldReader).IsAssignableFrom(t)) + .Where(t => t.GetConstructor(Type.EmptyTypes) != null) + .Select(Activator.CreateInstance) + .Cast() + .ToArray(); /// - /// Gets or sets the value type for the field + /// Gets or sets if the field is required on a given issue /// [JsonProperty] - public virtual TypeValue ValueType { get; set; } = TypeValue.Create(); + [DefaultValue(false)] + public bool Required { get; set; } /// /// Gets or sets the metadata for the values @@ -88,14 +77,10 @@ public IFieldReader[] Readers public virtual string ValueMetadata { get; set; } = string.Empty; /// - /// Determines if the field is valid + /// Gets or sets the value type for the field /// - /// - /// - public virtual bool IsValid(IField field) - { - return false; - } + [JsonProperty] + public virtual TypeValue ValueType { get; set; } = TypeValue.Create(); /// /// Creates a new issue field @@ -105,12 +90,27 @@ public virtual bool IsValid(IField field) /// public IField CreateField(Issue issue, FieldKey key) { - foreach (var provider in Readers) + foreach (IFieldReader provider in this.Readers) + { if (provider.CanCreateField(this)) + { return provider.CreateField(issue, key, this); + } + } + throw new IssueNotFoundException("Unable to create issue with given field"); } + /// + /// Determines if the field is valid + /// + /// + /// + public virtual bool IsValid(IField field) + { + return false; + } + /// /// Reads an existing issue field /// @@ -119,9 +119,14 @@ public IField CreateField(Issue issue, FieldKey key) /// public async Task ReadFieldAsync(Issue issue, FieldKey key) { - foreach (var provider in Readers) + foreach (IFieldReader provider in this.Readers) + { if (provider.CanReadField(this)) + { return await provider.ReadFieldAsync(issue, key, this); + } + } + throw new IssueNotFoundException("Unable to read issue with given field"); } } @@ -145,7 +150,7 @@ public FieldInfo() : this(false) /// public FieldInfo(bool required) : base(required) { - Required = required; + this.Required = required; } /// @@ -178,7 +183,7 @@ public FieldInfo() : this(false) /// public FieldInfo(bool required) : base(required) { - Required = required; + this.Required = required; } /// diff --git a/src/GitIssue/Fields/FieldKey.cs b/src/GitIssue/Fields/FieldKey.cs index d47a423..6e22c3e 100644 --- a/src/GitIssue/Fields/FieldKey.cs +++ b/src/GitIssue/Fields/FieldKey.cs @@ -26,7 +26,10 @@ private FieldKey(string key) public static FieldKey Create(string? key) { if (string.IsNullOrEmpty(key)) - return None; + { + return FieldKey.None; + } + return new FieldKey(key); } @@ -38,11 +41,13 @@ public static FieldKey Create(string? key) /// public bool Equals(FieldKey other) { - if (string.IsNullOrEmpty(key) && + if (string.IsNullOrEmpty(this.key) && string.IsNullOrEmpty(other.key)) + { return true; + } - return key == other.key; + return this.key == other.key; } /// @@ -82,28 +87,35 @@ public static implicit operator string(FieldKey value) /// public static implicit operator FieldKey(string value) { - return Create(value); + return FieldKey.Create(value); } /// public override string ToString() { - return key; + return this.key; } /// public override bool Equals(object? obj) { if (obj is FieldKey fieldKey) - return Equals(fieldKey); - if (obj is string str) return Equals(Create(str)); + { + return this.Equals(fieldKey); + } + + if (obj is string str) + { + return this.Equals(FieldKey.Create(str)); + } + return base.Equals(obj); } /// public override int GetHashCode() { - return key.GetHashCode(); + return this.key.GetHashCode(); } } } \ No newline at end of file diff --git a/src/GitIssue/Fields/FieldKeyTypeConverter.cs b/src/GitIssue/Fields/FieldKeyTypeConverter.cs index f7751d7..0a61fe9 100644 --- a/src/GitIssue/Fields/FieldKeyTypeConverter.cs +++ b/src/GitIssue/Fields/FieldKeyTypeConverter.cs @@ -13,7 +13,11 @@ public class FieldKeyTypeConverter : TypeConverter public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { - if (sourceType == typeof(string)) return true; + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); } @@ -21,7 +25,11 @@ public override bool CanConvertFrom(ITypeDescriptorContext? context, public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { - if (value is string str) return FieldKey.Create(str); + if (value is string str) + { + return FieldKey.Create(str); + } + return base.ConvertFrom(context, culture, value); } @@ -30,8 +38,13 @@ public override bool CanConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) + { if (value is FieldKey key) + { return key.ToString(); + } + } + return base.ConvertTo(context, culture, value, destinationType); } } diff --git a/src/GitIssue/Fields/FieldProvider.cs b/src/GitIssue/Fields/FieldProvider.cs index 097d9f5..4163302 100644 --- a/src/GitIssue/Fields/FieldProvider.cs +++ b/src/GitIssue/Fields/FieldProvider.cs @@ -29,17 +29,25 @@ public FieldProvider(Issue issue, FieldKey key, Func callback) } /// - public T AsValue() where T : IValue + public T[] AsArray() where T : IValue { - if (callback?.Invoke() is ValueField fileField) return fileField.Value; - return default!; + if (this.callback?.Invoke() is ArrayField fileField) + { + return fileField.Values; + } + + return null!; } /// - public T[] AsArray() where T : IValue + public T AsValue() where T : IValue { - if (callback?.Invoke() is ArrayField fileField) return fileField.Values; - return null!; + if (this.callback?.Invoke() is ValueField fileField) + { + return fileField.Value; + } + + return default(T)!; } } } \ No newline at end of file diff --git a/src/GitIssue/Fields/FieldReader.cs b/src/GitIssue/Fields/FieldReader.cs index 6ea767b..b402a5a 100644 --- a/src/GitIssue/Fields/FieldReader.cs +++ b/src/GitIssue/Fields/FieldReader.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Reflection; using System.Threading.Tasks; using GitIssue.Issues; @@ -17,7 +18,7 @@ public abstract class FieldReader : IFieldReader /// public IField CreateField(Issue issue, FieldKey key, FieldInfo info) { - var method = GetType().GetMethods() + MethodInfo? method = GetType().GetMethods() .Where(m => m.IsPublic) .Where(m => m.Name == nameof(CreateField)) .Where(m => m.IsGenericMethodDefinition) @@ -27,7 +28,7 @@ public IField CreateField(Issue issue, FieldKey key, FieldInfo info) if (method != null) { object[] args = { issue, key, info }; - var field = (IField)method.Invoke(this, args)!; + IField field = (IField)method.Invoke(this, args)!; return field; } @@ -40,7 +41,7 @@ public IField CreateField(Issue issue, FieldKey key, FieldInfo info) /// public async Task ReadFieldAsync(Issue issue, FieldKey key, FieldInfo info) { - var method = GetType().GetMethods() + MethodInfo? method = GetType().GetMethods() .Where(m => m.IsPublic) .Where(m => m.Name == nameof(ReadFieldAsync)) .Where(m => m.IsGenericMethodDefinition) @@ -50,9 +51,9 @@ public async Task ReadFieldAsync(Issue issue, FieldKey key, FieldInfo in if (method != null) { object[] args = { issue, key, info }; - var task = (Task)method.Invoke(this, args)!; + Task task = (Task)method.Invoke(this, args)!; await task; - var result = (IField)task.GetType().GetProperty("Result")?.GetValue(task)!; + IField result = (IField)task.GetType().GetProperty("Result")?.GetValue(task)!; return result; } diff --git a/src/GitIssue/Fields/IField.cs b/src/GitIssue/Fields/IField.cs index fd7764f..30754b0 100644 --- a/src/GitIssue/Fields/IField.cs +++ b/src/GitIssue/Fields/IField.cs @@ -9,22 +9,22 @@ namespace GitIssue.Fields public interface IField : IReadOnlyField { /// - /// Copy's the field + /// Copy's the field /// /// bool Copy([AllowNull] IField other); /// - /// Parses the input and updates the field if successful + /// Saves any additional filed data /// - /// /// - bool Update(string input); + Task SaveAsync(); /// - /// Saves any additional filed data + /// Parses the input and updates the field if successful /// + /// /// - Task SaveAsync(); + bool Update(string input); } } \ No newline at end of file diff --git a/src/GitIssue/Fields/IFieldFactory.cs b/src/GitIssue/Fields/IFieldFactory.cs index 9a5d9a1..3174fe9 100644 --- a/src/GitIssue/Fields/IFieldFactory.cs +++ b/src/GitIssue/Fields/IFieldFactory.cs @@ -7,6 +7,14 @@ namespace GitIssue.Fields /// public interface IFieldFactory { + /// + /// Updates the field with the specified array of values + /// + /// the field data type + /// the array of values + /// A new field that fulfills the interface + void WithArray(T[] values) where T : IValue; + /// /// /// @@ -20,13 +28,5 @@ public interface IFieldFactory /// the field value /// A new field that fulfills the interface void WithValue(T value) where T : IValue; - - /// - /// Updates the field with the specified array of values - /// - /// the field data type - /// the array of values - /// A new field that fulfills the interface - void WithArray(T[] values) where T : IValue; } } \ No newline at end of file diff --git a/src/GitIssue/Fields/IFieldProvider.cs b/src/GitIssue/Fields/IFieldProvider.cs index 2b0bf33..01f8581 100644 --- a/src/GitIssue/Fields/IFieldProvider.cs +++ b/src/GitIssue/Fields/IFieldProvider.cs @@ -8,17 +8,17 @@ namespace GitIssue.Fields public interface IFieldProvider { /// - /// Extracts the value from the field + /// Extracts the value array from the field /// /// the value data type - /// the field value, or default - T AsValue() where T : IValue; + /// the array of field values, or null + T[] AsArray() where T : IValue; /// - /// Extracts the value array from the field + /// Extracts the value from the field /// /// the value data type - /// the array of field values, or null - T[] AsArray() where T : IValue; + /// the field value, or default + T AsValue() where T : IValue; } } \ No newline at end of file diff --git a/src/GitIssue/Fields/IFieldReader.cs b/src/GitIssue/Fields/IFieldReader.cs index cd4503c..52d3193 100644 --- a/src/GitIssue/Fields/IFieldReader.cs +++ b/src/GitIssue/Fields/IFieldReader.cs @@ -13,14 +13,14 @@ public interface IFieldReader /// /// the field info /// true if can create - public bool CanCreateField(FieldInfo info); + bool CanCreateField(FieldInfo info); /// /// Gets a value indicating if this can read an existing field /// /// the field info /// true if can read - public bool CanReadField(FieldInfo info); + bool CanReadField(FieldInfo info); /// /// Creates a new field @@ -29,7 +29,7 @@ public interface IFieldReader /// the field key /// the field info /// - public IField CreateField(Issue issue, FieldKey key, FieldInfo info); + IField CreateField(Issue issue, FieldKey key, FieldInfo info); /// /// Creates a new field with specified data type @@ -39,7 +39,7 @@ public interface IFieldReader /// the field key /// the field info /// - public IField CreateField(Issue issue, FieldKey key, FieldInfo info); + IField CreateField(Issue issue, FieldKey key, FieldInfo info); /// /// Reads an existing field @@ -48,7 +48,7 @@ public interface IFieldReader /// the field key /// the field info /// - public Task ReadFieldAsync(Issue issue, FieldKey key, FieldInfo info); + Task ReadFieldAsync(Issue issue, FieldKey key, FieldInfo info); /// /// Reads an existing field with specified data type @@ -58,6 +58,6 @@ public interface IFieldReader /// the field key /// the field info /// - public Task ReadFieldAsync(Issue issue, FieldKey key, FieldInfo info); + Task ReadFieldAsync(Issue issue, FieldKey key, FieldInfo info); } } \ No newline at end of file diff --git a/src/GitIssue/Fields/Value/IValueField.cs b/src/GitIssue/Fields/Value/IValueField.cs index 236978e..d65eb07 100644 --- a/src/GitIssue/Fields/Value/IValueField.cs +++ b/src/GitIssue/Fields/Value/IValueField.cs @@ -8,14 +8,14 @@ namespace GitIssue.Fields.Value public interface IValueField : IField { /// - /// Gets the value type + /// Gets or sets the value /// - Type ValueType { get; } + object? Value { get; set; } /// - /// Gets or sets the value + /// Gets the value type /// - object? Value { get; set; } + Type ValueType { get; } /// /// Tries to parse the input into an array value diff --git a/src/GitIssue/Fields/Value/ValueField.cs b/src/GitIssue/Fields/Value/ValueField.cs index f3dc5fd..89193db 100644 --- a/src/GitIssue/Fields/Value/ValueField.cs +++ b/src/GitIssue/Fields/Value/ValueField.cs @@ -17,49 +17,31 @@ public abstract class ValueField : Field, IValueField /// the issue value protected ValueField(FieldKey key, T value) : base(key) { - Value = value; + this.Value = value; } /// - public Type ValueType => typeof(T); + public T Value { get; set; } /// - public T Value { get; set; } + public Type ValueType => typeof(T); object? IValueField.Value { - get => Value; + get => this.Value; set { if (value is T result) { - Value = result; + this.Value = result; } } } /// - public override bool Update(string input) - { - if (TryParse(input, out var result)) - { - Value = result; - return true; - } - return false; - } - - /// - bool IValueField.TryParse(string input, out object? value) + public override string ToString() { - if (ValueExtensions.TryParse(input, out T result)) - { - value = result; - return true; - } - - value = null; - return false; + return this.Value?.ToString()!; } /// @@ -69,14 +51,14 @@ public bool TryParse(string input, out T value) } /// - public override string ToString() + public override bool Equals([AllowNull] IField other) { - return Value?.ToString()!; - } + if (other is IValueField valueField) + { + return this.Value?.Equals(valueField.Value) ?? false; + } - bool IValueField.TryParse(string input, out T value) - { - throw new NotImplementedException(); + return false; } /// @@ -86,17 +68,38 @@ public override bool Copy([AllowNull] IField other) { this.Value = valueField.Value; } + return false; } /// - public override bool Equals([AllowNull] IField other) + public override bool Update(string input) { - if (other is IValueField valueField) + if (this.TryParse(input, out T result)) { - return this.Value?.Equals(valueField.Value) ?? false; + this.Value = result; + return true; } + return false; } + + /// + bool IValueField.TryParse(string input, out object? value) + { + if (ValueExtensions.TryParse(input, out T result)) + { + value = result; + return true; + } + + value = null; + return false; + } + + bool IValueField.TryParse(string input, out T value) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/GitIssue/Formatters/DelegateFormatter.cs b/src/GitIssue/Formatters/DelegateFormatter.cs index 49d9a5e..d6ec9d6 100644 --- a/src/GitIssue/Formatters/DelegateFormatter.cs +++ b/src/GitIssue/Formatters/DelegateFormatter.cs @@ -52,13 +52,13 @@ public DelegateFormatter(Func issueFormatter, Func public string? Format(IField field) { - return fieldFormatter?.Invoke(field); + return this.fieldFormatter?.Invoke(field); } /// public string? Format(IReadOnlyIssue issue) { - return issueFormatter?.Invoke(issue); + return this.issueFormatter?.Invoke(issue); } } } \ No newline at end of file diff --git a/src/GitIssue/Formatters/IssueFormatter.cs b/src/GitIssue/Formatters/IssueFormatter.cs index 3ba7f35..8094e86 100644 --- a/src/GitIssue/Formatters/IssueFormatter.cs +++ b/src/GitIssue/Formatters/IssueFormatter.cs @@ -12,23 +12,15 @@ namespace GitIssue.Formatters /// public class IssueFormatter : IIssueFormatter, IFieldFormatter { - - private readonly string matcher = @"([\%][\w\*]*)"; + private readonly Dictionary> fieldFormatters = + new Dictionary>(); private readonly string format; private readonly Dictionary> issueFormatters = - new Dictionary> - { - {"Key", (t,i) => i.Key.ToString()}, - {"*", (t,i) => i.Select(f => $"{f.Key,-16} {t.Format(f.Value)}").Aggregate((a, b) => $"{a}{Environment.NewLine}{b}") } - }; + new Dictionary> { { "Key", (t, i) => i.Key.ToString() }, { "*", (t, i) => i.Select(f => $"{f.Key,-16} {t.Format(f.Value)}").Aggregate((a, b) => $"{a}{Environment.NewLine}{b}") } }; - private readonly Dictionary> fieldFormatters = - new Dictionary> - { - - }; + private readonly string matcher = @"([\%][\w\*]*)"; /// /// Initializes a new instance of the class @@ -65,41 +57,57 @@ public IssueFormatter( /// /// Gets the default formatter /// - public static IssueFormatter Simple => new IssueFormatter(); + public static IssueFormatter Detailed => new IssueFormatter("%*"); /// /// Gets the default formatter /// - public static IssueFormatter Detailed => new IssueFormatter("%*"); + public static IssueFormatter Simple => new IssueFormatter(); + + /// + /// Tries to match the input with the compiled regex + /// + /// + /// + /// + /// + public static bool TryMatch(Regex regex, string input, out Match match) + { + match = regex.Match(input); + return match.Success; + } /// public virtual string Format(IField field) { - if (fieldFormatters.TryGetValue(field.Key, out var formatter)) + if (this.fieldFormatters.TryGetValue(field.Key, out Func? formatter)) { return formatter.Invoke(this, field); } + return field.ToString() ?? string.Empty; } /// public virtual string Format(IReadOnlyIssue issue) { - var result = format; - var regex = new Regex(matcher, RegexOptions.Compiled); - foreach (Match? match in regex.Matches(format)) + string result = this.format; + Regex regex = new Regex(this.matcher, RegexOptions.Compiled); + foreach (Match? match in regex.Matches(this.format)) { if (match == null) + { continue; + } - var property = match.Value.TrimStart('%'); - if (issueFormatters.TryGetValue(property, out var formatter)) + string property = match.Value.TrimStart('%'); + if (this.issueFormatters.TryGetValue(property, out Func? formatter)) { result = result.Replace(match.Value, formatter.Invoke(this, issue)); continue; } - if (issue.TryGetValue(FieldKey.Create(property), out var field)) + if (issue.TryGetValue(FieldKey.Create(property), out IField? field)) { result = result.Replace(match.Value, this.Format(field)); } @@ -107,18 +115,5 @@ public virtual string Format(IReadOnlyIssue issue) return result; } - - /// - /// Tries to match the input with the compiled regex - /// - /// - /// - /// - /// - public static bool TryMatch(Regex regex, string input, out Match match) - { - match = regex.Match(input); - return match.Success; - } } } \ No newline at end of file diff --git a/src/GitIssue/GitIssue.csproj b/src/GitIssue/GitIssue.csproj index 72d6ba0..c8472f6 100644 --- a/src/GitIssue/GitIssue.csproj +++ b/src/GitIssue/GitIssue.csproj @@ -1,21 +1,21 @@  - net6.0 + net10.0 enable latest - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + \ No newline at end of file diff --git a/src/GitIssue/GitIssueModule.cs b/src/GitIssue/GitIssueModule.cs index 9bd152c..e3628b2 100644 --- a/src/GitIssue/GitIssueModule.cs +++ b/src/GitIssue/GitIssueModule.cs @@ -9,11 +9,11 @@ namespace GitIssue { /// - /// Dependency Injection Registrations + /// Dependency Injection Registrations /// public class GitIssueModule : Module { - /// + /// protected override void Load(ContainerBuilder builder) { // Issue manager @@ -58,7 +58,7 @@ protected override void Load(ContainerBuilder builder) RepositoryRoot root = c.Resolve(); IIssueKeyProvider provider = c.Resolve(); IIssueConfiguration configuration = c.Resolve(); - IssueCreation creation = (key) => new FileIssue(new IssueRoot(root, key, provider.GetIssuePath(key)), configuration.Fields); + IssueCreation creation = key => new FileIssue(new IssueRoot(root, key, provider.GetIssuePath(key)), configuration.Fields); return creation; }) .AsSelf() @@ -68,7 +68,7 @@ protected override void Load(ContainerBuilder builder) { RepositoryRoot root = c.Resolve(); IIssueKeyProvider provider = c.Resolve(); - IssueDeletion deletion = (key) => FileIssue.DeleteAsync(new IssueRoot(root, key, provider.GetIssuePath(key))); + IssueDeletion deletion = key => FileIssue.DeleteAsync(new IssueRoot(root, key, provider.GetIssuePath(key))); return deletion; }) .AsSelf() @@ -79,7 +79,7 @@ protected override void Load(ContainerBuilder builder) RepositoryRoot root = c.Resolve(); IIssueKeyProvider provider = c.Resolve(); IIssueConfiguration configuration = c.Resolve(); - IssueLoading read = (key) => FileIssue.ReadAsync(new IssueRoot(root, key, provider.GetIssuePath(key)), configuration.Fields); + IssueLoading read = key => FileIssue.ReadAsync(new IssueRoot(root, key, provider.GetIssuePath(key)), configuration.Fields); return read; }) .AsSelf() @@ -90,4 +90,4 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().AsSelf(); } } -} +} \ No newline at end of file diff --git a/src/GitIssue/IChangeLog.cs b/src/GitIssue/IChangeLog.cs index 6508dda..a7dcb16 100644 --- a/src/GitIssue/IChangeLog.cs +++ b/src/GitIssue/IChangeLog.cs @@ -11,19 +11,14 @@ public interface IChangeLog /// /// Gets the dictionary of changes /// - public Dictionary> Log { get; set; } - - /// - /// Clears all changes in the log - /// - public void Clear(); + Dictionary> Log { get; set; } /// /// Records a new change in the log /// /// /// - public void Add(IssueKey key, ChangeType change); + void Add(IssueKey key, ChangeType change); /// /// Records a new change in the log @@ -31,14 +26,14 @@ public interface IChangeLog /// /// /// - public void Add(IssueKey key, ChangeType change, string summary); + void Add(IssueKey key, ChangeType change, string summary); /// /// Records a new change in the log /// /// /// - public void Add(IIssue issue, ChangeType change); + void Add(IIssue issue, ChangeType change); /// /// Records a new change in the log @@ -46,12 +41,17 @@ public interface IChangeLog /// /// /// - public void Add(IIssue issue, ChangeType change, string summary); + void Add(IIssue issue, ChangeType change, string summary); + + /// + /// Clears all changes in the log + /// + void Clear(); /// - /// Saved the change log to a file + /// Saved the change log to a file /// /// - public void Save(string file); + void Save(string file); } } \ No newline at end of file diff --git a/src/GitIssue/IIssueConfiguration.cs b/src/GitIssue/IIssueConfiguration.cs index b15b8a8..62dca17 100644 --- a/src/GitIssue/IIssueConfiguration.cs +++ b/src/GitIssue/IIssueConfiguration.cs @@ -10,14 +10,14 @@ namespace GitIssue public interface IIssueConfiguration { /// - /// Gets or sets the key provider + /// Gets or sets the dictionary of fields /// - TypeValue KeyProvider { get; set; } + Dictionary Fields { get; set; } /// - /// Gets or sets the dictionary of fields + /// Gets or sets the key provider /// - Dictionary Fields { get; set; } + TypeValue KeyProvider { get; set; } /// /// Saves the configuration to a file diff --git a/src/GitIssue/IIssueManager.cs b/src/GitIssue/IIssueManager.cs index 69d7a44..8c1e866 100644 --- a/src/GitIssue/IIssueManager.cs +++ b/src/GitIssue/IIssueManager.cs @@ -12,39 +12,39 @@ namespace GitIssue public interface IIssueManager : IAsyncDisposable, IDisposable { /// - /// Gets the working directory + /// Gets the change log for the issues /// - string WorkingDirectory { get; } + IChangeLog Changes { get; } /// - /// Gets the repository root + /// Gets the issue configuration /// - public RepositoryRoot Root { get; } + IIssueConfiguration Configuration { get; } /// - /// Gets the GIT repository + /// Gets the issue key provider /// - IRepository Repository { get; } + IIssueKeyProvider KeyProvider { get; } /// - /// Gets the issue configuration + /// Gets the GIT repository /// - IIssueConfiguration Configuration { get; } + IRepository Repository { get; } /// - /// Gets the change log for the issues + /// Gets the repository root /// - IChangeLog Changes { get; } + RepositoryRoot Root { get; } /// - /// Gets the tracked issue + /// Gets the tracked issue /// ITrackedIssue Tracked { get; } /// - /// Gets the issue key provider + /// Gets the working directory /// - IIssueKeyProvider KeyProvider { get; } + string WorkingDirectory { get; } /// /// Commits the changes @@ -88,20 +88,6 @@ public interface IIssueManager : IAsyncDisposable, IDisposable /// Task CreateAsync(string title, string description); - /// - /// Finds an issue - /// - /// the predicate to evaluate - /// - IEnumerable Find(Func predicated); - - /// - /// Finds an issue asynchronously. - /// - /// the predicate to evaluate the issue against - /// - IAsyncEnumerable FindAsync(Func predicated); - /// /// Deletes an existing issue /// @@ -128,6 +114,20 @@ public interface IIssueManager : IAsyncDisposable, IDisposable /// Task DeleteAsync(IssueKey key); + /// + /// Finds an issue + /// + /// the predicate to evaluate + /// + IEnumerable Find(Func predicated); + + /// + /// Finds an issue asynchronously. + /// + /// the predicate to evaluate the issue against + /// + IAsyncEnumerable FindAsync(Func predicated); + /// /// Tracks the issue /// @@ -141,6 +141,5 @@ public interface IIssueManager : IAsyncDisposable, IDisposable /// /// Task TrackAsync(IssueKey key); - } } \ No newline at end of file diff --git a/src/GitIssue/IssueConfiguration.cs b/src/GitIssue/IssueConfiguration.cs index dbf1f49..74303fe 100644 --- a/src/GitIssue/IssueConfiguration.cs +++ b/src/GitIssue/IssueConfiguration.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; using GitIssue.Fields; using GitIssue.Issues; using GitIssue.Issues.File; @@ -23,53 +21,27 @@ public class IssueConfiguration : IIssueConfiguration /// public IssueConfiguration() { - KeyProvider = TypeValue.Create(); - Fields = new Dictionary + this.KeyProvider = TypeValue.Create(); + this.Fields = new Dictionary { - {FieldKey.Create("Key"), new FieldInfo(true)}, - {FieldKey.Create("Title"), new FieldInfo(true)}, - {FieldKey.Create("Description"), new FieldInfo(true)}, - {FieldKey.Create("Author"), new FieldInfo(true)}, - {FieldKey.Create("Created"), new FieldInfo(true)}, - {FieldKey.Create("Updated"), new FieldInfo(true)}, - {FieldKey.Create("Labels"), new FieldInfo(false)}, - {FieldKey.Create("Comments"), new FieldInfo(false)} + { FieldKey.Create("Key"), new FieldInfo(true) }, + { FieldKey.Create("Title"), new FieldInfo(true) }, + { FieldKey.Create("Description"), new FieldInfo(true) }, + { FieldKey.Create("Author"), new FieldInfo(true) }, + { FieldKey.Create("Created"), new FieldInfo(true) }, + { FieldKey.Create("Updated"), new FieldInfo(true) }, + { FieldKey.Create("Labels"), new FieldInfo(false) }, + { FieldKey.Create("Comments"), new FieldInfo(false) }, }; } - /// - [JsonProperty] - public TypeValue KeyProvider { get; set; } - - /// + /// [JsonProperty] public Dictionary Fields { get; set; } - /// - public void Save(string file) - { - Save(file); - } - - /// - public void Save(string file) where T : IssueConfiguration, new() - { - try - { - using var stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); - using var writer = new StreamWriter(stream); - var serializer = JsonSerializer.Create(new JsonSerializerSettings - { - Formatting = Formatting.Indented, - DefaultValueHandling = DefaultValueHandling.Ignore - }); - serializer.Serialize(writer, this, typeof(T)); - } - catch (Exception ex) - { - throw new AggregateException($"Unable to serialize {file} as config file ", ex); - } - } + /// + [JsonProperty] + public TypeValue KeyProvider { get; set; } /// /// Reads the configuration from a file @@ -78,7 +50,7 @@ public void Save(string file) /// the public static IssueConfiguration Read(string file) { - return Read(file); + return IssueConfiguration.Read(file); } /// @@ -93,8 +65,8 @@ public static IssueConfiguration Read(string file) { using var stream = new FileStream(file, FileMode.Open, FileAccess.Read); using var reader = new StreamReader(stream); - var serializer = new JsonSerializer(); - var configuration = (T)serializer.Deserialize(reader, typeof(T))!; + JsonSerializer serializer = new JsonSerializer(); + T configuration = (T)serializer.Deserialize(reader, typeof(T))!; return configuration; } catch (Exception ex) @@ -102,5 +74,27 @@ public static IssueConfiguration Read(string file) throw new AggregateException($"Unable to deserialize {file} as config file ", ex); } } + + /// + public void Save(string file) + { + this.Save(file); + } + + /// + public void Save(string file) where T : IssueConfiguration, new() + { + try + { + using var stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); + using var writer = new StreamWriter(stream); + JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings { Formatting = Formatting.Indented, DefaultValueHandling = DefaultValueHandling.Ignore }); + serializer.Serialize(writer, this, typeof(T)); + } + catch (Exception ex) + { + throw new AggregateException($"Unable to serialize {file} as config file ", ex); + } + } } } \ No newline at end of file diff --git a/src/GitIssue/IssueManager.cs b/src/GitIssue/IssueManager.cs index 1cc8346..262b152 100644 --- a/src/GitIssue/IssueManager.cs +++ b/src/GitIssue/IssueManager.cs @@ -11,19 +11,19 @@ namespace GitIssue { /// - /// Delegates for callback on disposal + /// Delegates for callback on disposal /// public delegate void IssueManagerDisposal(IIssueManager manager); /// - /// Delegate for issue creation + /// Delegate for issue creation /// /// /// public delegate IIssue IssueCreation(IssueKey key); /// - /// Delegate for issue deletion + /// Delegate for issue deletion /// /// /// @@ -31,7 +31,7 @@ namespace GitIssue /// - /// Delegate for issue deletion + /// Delegate for issue deletion /// /// /// @@ -42,42 +42,14 @@ namespace GitIssue /// public class IssueManager : IIssueManager { - private bool disposed = false; - - private readonly ILogger? logger; - private readonly IssueCreation issueCreation; private readonly IssueDeletion issueDeletion; private readonly IssueLoading issueLoading; - /// - /// Initializes a new instance of the mass - /// - public static IIssueManager Open(string directory) => Open(directory, Paths.IssueRootFolderName); - - /// - /// Initializes a new instance of the mass - /// - public static IIssueManager Open(string directory, string name) - { - var builder = new ContainerBuilder(); - var root = RepositoryRoot.Open(directory, name); - builder.Register(c => root).As().SingleInstance(); - return Open(builder); - } - - /// - /// Initializes a new instance of the mass - /// - public static IIssueManager Open(ContainerBuilder builder) - { - builder.RegisterModule(); - IContainer container = builder.Build(); - var manager = container.Resolve(); - return manager; - } + private readonly ILogger? logger; + private bool disposed; /// /// Initializes a new instance of the mass @@ -107,26 +79,175 @@ public IssueManager( this.issueLoading = issueLoading; } + /// + public void Dispose() + { + if (this.disposed) + { + return; + } + + this.Changes.Save(this.Root.ChangeLog); + this.disposed = true; + } + + /// + public IChangeLog Changes { get; protected set; } + + /// + public IIssueConfiguration Configuration { get; protected set; } + /// /// Gets or sets the key provider /// public IIssueKeyProvider KeyProvider { get; protected set; } + /// + public IRepository Repository { get; protected set; } + /// /// Gets or sets the repository root /// public RepositoryRoot Root { get; protected set; } + /// + public ITrackedIssue Tracked { get; protected set; } + /// /// Gets or sets the working directory /// public string WorkingDirectory { get; protected set; } - /// - public IIssueConfiguration Configuration { get; protected set; } + /// + /// Initializes a new issue manager + /// + /// the issue manager + public static IIssueManager Init() + { + return IssueManager.Init(new IssueConfiguration()); + } + + /// + /// Initializes a new issue manager + /// + /// + /// + public static IIssueManager Init(string directory) + { + return IssueManager.Init(new IssueConfiguration(), directory); + } + + /// + /// Initializes a new issue manager + /// + /// + /// + /// + public static IIssueManager Init(string directory, string name) + { + return IssueManager.Init(new IssueConfiguration(), directory, name); + } + + /// + /// Initializes a new issue manager + /// + /// + /// + public static IIssueManager Init(IssueConfiguration configuration) + { + return IssueManager.Init(configuration, Environment.CurrentDirectory); + } + + /// + /// Initializes a new issue manager + /// + /// + /// + /// + public static IIssueManager Init(IssueConfiguration configuration, string directory) + { + return IssueManager.Init(configuration, directory, Paths.IssueRootFolderName); + } + + /// + /// Initializes a new issue manager + /// + /// + /// + /// + /// + public static IIssueManager Init(IssueConfiguration configuration, string directory, string name) + { + if (configuration == null) + { + throw new ArgumentException(nameof(configuration)); + } + + if (directory == null) + { + throw new ArgumentException(nameof(directory)); + } + + if (name == null) + { + throw new ArgumentException(nameof(name)); + } + + RepositoryRoot root = RepositoryRoot.Create(directory, name); + configuration.Save(root.ConfigFile); + + ContainerBuilder builder = new ContainerBuilder(); + builder.Register(c => root).As().SingleInstance(); + builder.Register(c => configuration).As().SingleInstance(); + builder.RegisterModule(); + IContainer container = builder.Build(); + + return container.Resolve(); + } + + /// + /// Initializes a new instance of the mass + /// + public static IIssueManager Open(string directory) + { + return IssueManager.Open(directory, Paths.IssueRootFolderName); + } + + /// + /// Initializes a new instance of the mass + /// + public static IIssueManager Open(string directory, string name) + { + ContainerBuilder builder = new ContainerBuilder(); + RepositoryRoot root = RepositoryRoot.Open(directory, name); + builder.Register(c => root).As().SingleInstance(); + return IssueManager.Open(builder); + } + + /// + /// Initializes a new instance of the mass + /// + public static IIssueManager Open(ContainerBuilder builder) + { + builder.RegisterModule(); + IContainer container = builder.Build(); + IssueManager manager = container.Resolve(); + return manager; + } /// - public IChangeLog Changes { get; protected set; } + public ValueTask DisposeAsync() + { + try + { + this.Dispose(); + return default(ValueTask); + } + catch (Exception exception) + { + return new ValueTask(Task.FromException(exception)); + } + } /// public bool Commit() @@ -139,107 +260,106 @@ public bool Commit() /// public Task CommitAsync() { - if (Repository.Index.IsFullyMerged == false) + if (!this.Repository.Index.IsFullyMerged) { - logger?.Error("Cannot commit, requires merge"); + this.logger?.Error("Cannot commit, requires merge"); return Task.FromResult(false); } - if (Repository.RetrieveStatus().Staged.Any()) + if (this.Repository.RetrieveStatus().Staged.Any()) { - logger?.Error("Cannot commit, another commit is in progress"); + this.logger?.Error("Cannot commit, another commit is in progress"); return Task.FromResult(false); } - foreach (var item in Repository.RetrieveStatus(new StatusOptions() - { - PathSpec = new[] { $"{Path.GetRelativePath(Root.RootPath, Root.IssuesPath)}/*" }, - IncludeIgnored = true, - IncludeUntracked = true, - RecurseIgnoredDirs = true, - RecurseUntrackedDirs = true, - })) + foreach (StatusEntry? item in this.Repository.RetrieveStatus(new StatusOptions + { + PathSpec = new[] { $"{Path.GetRelativePath(this.Root.RootPath, this.Root.IssuesPath)}/*" }, + IncludeIgnored = true, + IncludeUntracked = true, + RecurseIgnoredDirs = true, + RecurseUntrackedDirs = true, + })) { - bool ignored = Repository.Ignore.IsPathIgnored(Root.Name); - var relative = item.FilePath; + bool ignored = this.Repository.Ignore.IsPathIgnored(item.FilePath); + string? relative = item.FilePath; - if (Path.GetFullPath(relative).Equals(Path.GetFullPath(Root.ChangeLog))) + if (Path.GetFullPath(relative).Equals(Path.GetFullPath(this.Root.ChangeLog))) + { continue; + } - if (Path.GetFullPath(relative).Equals(Path.GetFullPath(Root.Tracked))) + if (Path.GetFullPath(relative).Equals(Path.GetFullPath(this.Root.Tracked))) + { continue; + } - if (ignored && (item.State & FileStatus.Ignored) != 0) + if (ignored && ((item.State & FileStatus.Ignored) != 0)) { if (Directory.Exists(Path.GetFullPath(item.FilePath))) + { continue; - Repository.Index.Add(item.FilePath); + } + + this.Repository.Index.Add(item.FilePath); } - if ((item.State & FileStatus.NewInWorkdir) != 0 || - (item.State & FileStatus.ModifiedInWorkdir) != 0) - Repository.Index.Add(item.FilePath); + if (((item.State & FileStatus.NewInWorkdir) != 0) || + ((item.State & FileStatus.ModifiedInWorkdir) != 0)) + { + this.Repository.Index.Add(item.FilePath); + } if ((item.State & FileStatus.DeletedFromWorkdir) != 0) - Repository.Index.Remove(item.FilePath); + { + this.Repository.Index.Remove(item.FilePath); + } } - Repository.Index.Write(); + this.Repository.Index.Write(); - string comments = Changes.GenerateComments(); - var config = Repository.Config; - var author = config.BuildSignature(DateTimeOffset.Now); - Repository.Commit(comments, author, author); - Changes.Clear(); + string comments = this.Changes.GenerateComments(); + Configuration? config = this.Repository.Config; + Signature? author = config.BuildSignature(DateTimeOffset.Now); + this.Repository.Commit(comments, author, author); + this.Changes.Clear(); return Task.FromResult(true); } /// - public IRepository Repository { get; protected set; } + public IIssue Create(string title) + { + return this.CreateAsync(title) + .WithSafeResult() + .GetResult(); + } /// - public ITrackedIssue Tracked { get; protected set; } + public IIssue Create(string title, string description) + { + return this.CreateAsync(title, description) + .WithSafeResult() + .GetResult(); + } /// public async Task CreateAsync(string title) { - return await CreateAsync(title, string.Empty); + return await this.CreateAsync(title, string.Empty); } /// public async Task CreateAsync(string title, string description) { - IIssue issue = this.issueCreation(KeyProvider.Next()); + IIssue issue = this.issueCreation(this.KeyProvider.Next()); issue.Title = title; issue.Description = description; issue.Author = this.Repository.Config.BuildSignature(issue.Created.Item); await issue.SaveAsync(); - Changes.Add(issue, ChangeType.Create); + this.Changes.Add(issue, ChangeType.Create); return issue; } - /// - public IEnumerable Find(Func predicate) - { - var issues = new List(); - Task.Run(async () => - { - await foreach (var issue in FindAsync(predicate)) issues.Add(issue); - }).Wait(); - return issues; - } - - /// - public async IAsyncEnumerable FindAsync(Func predicated) - { - foreach (var key in KeyProvider.Keys) - { - var issue = await this.issueLoading(key); - if (issue != null && predicated.Invoke(issue)) - yield return issue; - } - } - /// public bool Delete(string id) @@ -262,13 +382,13 @@ public bool Delete(IssueKey key) /// public async Task DeleteAsync(string id) { - if (KeyProvider.TryGetKey(id, out var key)) + if (this.KeyProvider.TryGetKey(id, out IssueKey key)) { - await DeleteAsync(key); + await this.DeleteAsync(key); return true; } - logger?.Error($"Failed to get issue key from {id}"); + this.logger?.Error($"Failed to get issue key from {id}"); return false; } @@ -276,125 +396,48 @@ public async Task DeleteAsync(string id) /// public async Task DeleteAsync(IssueKey key) { - if (await this.issueDeletion(key)) + bool success = await this.issueDeletion(key); + if (success) { this.Changes.Add(key, ChangeType.Delete); } - return true; - } - /// - public void Dispose() - { - if (disposed) return; - Changes.Save(Root.ChangeLog); - disposed = true; + return success; } /// - public IIssue Create(string title) - { - return this.CreateAsync(title) - .WithSafeResult() - .GetResult(); - } - - /// - public IIssue Create(string title, string description) + public IEnumerable Find(Func predicate) { - return this.CreateAsync(title, description) - .WithSafeResult() - .GetResult(); + List issues = new List(); + Task.Run(async () => + { + await foreach (IIssue issue in this.FindAsync(predicate)) + { + issues.Add(issue); + } + }).Wait(); + return issues; } /// - public ValueTask DisposeAsync() + public async IAsyncEnumerable FindAsync(Func predicated) { - try - { - Dispose(); - return default; - } - catch (Exception exception) + foreach (IssueKey key in this.KeyProvider.Keys) { - return new ValueTask(Task.FromException(exception)); + IIssue? issue = await this.issueLoading(key); + if ((issue != null) && predicated.Invoke(issue)) + { + yield return issue; + } } } - /// - /// Initializes a new issue manager - /// - /// the issue manager - public static IIssueManager Init() - { - return Init(new IssueConfiguration()); - } - - /// - /// Initializes a new issue manager - /// - /// - /// - public static IIssueManager Init(string directory) - { - return Init(new IssueConfiguration(), directory); - } - - /// - /// Initializes a new issue manager - /// - /// - /// - /// - public static IIssueManager Init(string directory, string name) - { - return Init(new IssueConfiguration(), directory, name); - } - - /// - /// Initializes a new issue manager - /// - /// - /// - public static IIssueManager Init(IssueConfiguration configuration) - { - return Init(configuration, Environment.CurrentDirectory); - } - - /// - /// Initializes a new issue manager - /// - /// - /// - /// - public static IIssueManager Init(IssueConfiguration configuration, string directory) - { - return Init(configuration, directory, Paths.IssueRootFolderName); - } - - /// - /// Initializes a new issue manager - /// - /// - /// - /// - /// - public static IIssueManager Init(IssueConfiguration configuration, string directory, string name) + /// + public bool Track(IssueKey key) { - if (configuration == null) throw new ArgumentException(nameof(configuration)); - if (directory == null) throw new ArgumentException(nameof(directory)); - if (name == null) throw new ArgumentException(nameof(name)); - - var root = RepositoryRoot.Create(directory, name); - configuration.Save(root.ConfigFile); - - var builder = new ContainerBuilder(); - builder.Register(c => root).As().SingleInstance(); - builder.Register(c => configuration).As().SingleInstance(); - builder.RegisterModule(); - var container = builder.Build(); - - return container.Resolve(); + return this.TrackAsync(key) + .WithSafeResult() + .GetResult(); } /// @@ -403,18 +446,11 @@ public async Task TrackAsync(IssueKey key) if (this.Tracked.Key != key) { this.Tracked = new TrackedIssue(key); - await this.Tracked.SaveAsync(Root.Tracked, this.logger); + await this.Tracked.SaveAsync(this.Root.Tracked, this.logger); return false; } - return true; - } - /// - public bool Track(IssueKey key) - { - return this.TrackAsync(key) - .WithSafeResult() - .GetResult(); + return true; } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/File/FileArrayField.cs b/src/GitIssue/Issues/File/FileArrayField.cs index c44b8f8..bcc588b 100644 --- a/src/GitIssue/Issues/File/FileArrayField.cs +++ b/src/GitIssue/Issues/File/FileArrayField.cs @@ -28,15 +28,15 @@ public static async Task ReadAsync(IssueRoot issueRoot, FieldKey key, Fi { try { - var generic = typeof(FileArrayField<>); - var specific = generic.MakeGenericType((Type)info.ValueType); - var read = specific.GetMethod(nameof(ReadAsync), BindingFlags.Public | BindingFlags.Static); + Type generic = typeof(FileArrayField<>); + Type specific = generic.MakeGenericType((Type)info.ValueType); + MethodInfo? read = specific.GetMethod(nameof(FileArrayField.ReadAsync), BindingFlags.Public | BindingFlags.Static); if (read != null) { object[] args = { issueRoot, key, info }; - var task = (Task)read.Invoke(null, args)!; + Task task = (Task)read.Invoke(null, args)!; await task; - var result = (IField)task.GetType().GetProperty("Result")?.GetValue(task)!; + IField result = (IField)task.GetType().GetProperty("Result")?.GetValue(task)!; return result; } @@ -71,44 +71,7 @@ public FileArrayField(IssueRoot issueRoot, FieldKey key, T[] values) /// /// Gets the directory used to save the fields values /// - public string DirectoryPath => Path.Combine(issueRoot.IssuePath, Key.ToString()); - - /// - public override async Task SaveAsync() - { - try - { - if (Directory.Exists(DirectoryPath) == false) - Directory.CreateDirectory(DirectoryPath); - - var count = 0; - foreach (var value in Values) - { - var file = Path.Combine(DirectoryPath, count.ToString()); - await using var stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); - await using var writer = new StreamWriter(stream); - await writer.WriteAsync(value?.ToString()); - } - - return true; - } - catch (Exception e) - { - throw new ArgumentException($"Failed to save field {Key} on issue {issueRoot.Key}", e); - } - } - - /// - public override Task ExportAsync() - { - throw new NotImplementedException(); - } - - /// - public JToken ToJson() - { - return new JArray(Values); - } + public string DirectoryPath => Path.Combine(this.issueRoot.IssuePath, this.Key.ToString()); /// /// Asynchronously reads the field from disk @@ -121,17 +84,19 @@ public static async Task ReadAsync(IssueRoot issueRoot, FieldKey key, Fi { try { - var directory = Path.Combine(issueRoot.IssuePath, key.ToString()); - var field = new FileArrayField(issueRoot, key, new T[] { }); + string directory = Path.Combine(issueRoot.IssuePath, key.ToString()); + FileArrayField field = new FileArrayField(issueRoot, key, new T[] { }); - if (Directory.Exists(directory) == false) + if (!Directory.Exists(directory)) + { return field; + } - var values = new List(); - foreach (var fieldFile in Directory.EnumerateFiles(directory)) + List values = new List(); + foreach (string fieldFile in Directory.EnumerateFiles(directory)) { - var content = await System.IO.File.ReadAllTextAsync(fieldFile); - if (field.TryParse(content, out var result)) + string content = await System.IO.File.ReadAllTextAsync(fieldFile); + if (field.TryParse(content, out T result)) { field.Add(result); continue; @@ -147,5 +112,44 @@ public static async Task ReadAsync(IssueRoot issueRoot, FieldKey key, Fi throw new ArgumentException($"Failed to read field {key} on issue {issueRoot.Key}", e); } } + + /// + public override async Task SaveAsync() + { + try + { + if (!Directory.Exists(this.DirectoryPath)) + { + Directory.CreateDirectory(this.DirectoryPath); + } + + int count = 0; + foreach (T value in this.Values) + { + string file = Path.Combine(this.DirectoryPath, count.ToString()); + await using FileStream stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); + await using StreamWriter writer = new StreamWriter(stream); + await writer.WriteAsync(value?.ToString()); + } + + return true; + } + catch (Exception e) + { + throw new ArgumentException($"Failed to save field {this.Key} on issue {this.issueRoot.Key}", e); + } + } + + /// + public JToken ToJson() + { + return new JArray(this.Values); + } + + /// + public override Task ExportAsync() + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/File/FileIssue.cs b/src/GitIssue/Issues/File/FileIssue.cs index e5ac166..2e29f29 100644 --- a/src/GitIssue/Issues/File/FileIssue.cs +++ b/src/GitIssue/Issues/File/FileIssue.cs @@ -29,19 +29,23 @@ public FileIssue(IssueRoot root, IDictionary fields) : { } - /// - public override async Task SaveAsync() + /// + /// Deletes an issue and all it's fields from disk. + /// + /// + /// + public new static async Task DeleteAsync(IssueRoot issueRoot) { - // Save the json file - await base.SaveAsync(); + bool result = await JsonIssue.DeleteAsync(issueRoot); + if (!result) + { + return false; + } - // Save each modified field - foreach (var field in Values) - if (modifiedFields.Contains(field.Key)) - { - await field.SaveAsync(); - modifiedFields.Remove(field.Key); - } + if (Directory.Exists(issueRoot.IssuePath)) + { + Directory.Delete(issueRoot.IssuePath, true); + } return true; } @@ -55,32 +59,36 @@ public override async Task SaveAsync() public new static async Task ReadAsync(IssueRoot root, IDictionary fields) { - if (Directory.Exists(root.IssuePath) == false) + if (!Directory.Exists(root.IssuePath)) + { return null; + } - var issue = new FileIssue(root, fields); - foreach (var key in fields.Keys) + FileIssue issue = new FileIssue(root, fields); + foreach (FieldKey key in fields.Keys) { - var valueField = await fields[key].ReadFieldAsync(issue, key); + IField valueField = await fields[key].ReadFieldAsync(issue, key); issue.fields[key] = valueField; } return issue; } - /// - /// Deletes an issue and all it's fields from disk. - /// - /// - /// - public new static async Task DeleteAsync(IssueRoot issueRoot) + /// + public override async Task SaveAsync() { - bool result = await JsonIssue.DeleteAsync(issueRoot); - if (result == false) - return false; + // Save the json file + await base.SaveAsync(); - if (Directory.Exists(issueRoot.IssuePath)) - Directory.Delete(issueRoot.IssuePath, true); + // Save each modified field + foreach (IField field in this.Values) + { + if (this.modifiedFields.Contains(field.Key)) + { + await field.SaveAsync(); + this.modifiedFields.Remove(field.Key); + } + } return true; } diff --git a/src/GitIssue/Issues/File/FileIssueKeyProvider.cs b/src/GitIssue/Issues/File/FileIssueKeyProvider.cs index 46a3d52..6638b82 100644 --- a/src/GitIssue/Issues/File/FileIssueKeyProvider.cs +++ b/src/GitIssue/Issues/File/FileIssueKeyProvider.cs @@ -12,7 +12,7 @@ namespace GitIssue.Issues.File /// public class FileIssueKeyProvider : IssueKeyProvider { - private static char separator = '-'; + private static readonly char separator = '-'; private readonly RepositoryRoot root; @@ -26,79 +26,50 @@ public FileIssueKeyProvider(RepositoryRoot root) } /// - public override IEnumerable Keys => FindAll(root.IssuesPath); + public override IEnumerable Keys => this.FindAll(this.root.IssuesPath); /// public override string GetIssuePath(IssueKey key) { - return key.ToString().Replace(separator, Path.DirectorySeparatorChar); + return key.ToString().Replace(FileIssueKeyProvider.separator, Path.DirectorySeparatorChar); } /// public override IssueKey Next() { - var created = DateTime.Now; - string[] values = - { - created.Year.ToString("D4"), - created.Month.ToString("D2"), - created.Day.ToString("D2"), - GetUniqueId(8) - }; - var key = string.Join(separator, values); + DateTime created = DateTime.Now; + string[] values = { created.Year.ToString("D4"), created.Month.ToString("D2"), created.Day.ToString("D2"), this.GetUniqueId(8) }; + string key = string.Join(FileIssueKeyProvider.separator, values); return IssueKey.Create(key); } - private string GetUniqueId(int length) - { - using var sha = SHA256.Create(); - var checksum = sha.ComputeHash(Guid.NewGuid().ToByteArray()); - var key = BitConverter.ToString(checksum) - .Replace("-", "") - .ToUpperInvariant() - .Substring(0, length); - return key; - } - - private IEnumerable FindAll(string directory) + /// + /// Tries to get the commit of the current branch + /// + /// + /// + public bool TryGetGitCommit(out string commit) { - var keys = new List(); - - if (Directory.Exists(root.IssuesPath) == false) - return keys; - - foreach (var year in Directory.EnumerateDirectories(root.IssuesPath) - .Select(d => new DirectoryInfo(d))) - foreach (var month in Directory.EnumerateDirectories(year.FullName) - .Select(d => new DirectoryInfo(d))) - foreach (var day in Directory.EnumerateDirectories(month.FullName) - .Select(d => new DirectoryInfo(d))) - foreach (var id in Directory.EnumerateDirectories(day.FullName) - .Select(d => new DirectoryInfo(d))) - { - var path = Path.Combine(year.Name, month.Name, day.Name, id.Name); - if (TryGetKey(path, out var key)) - keys.Add(key); - } - - return keys; + using IRepository repository = this.root.GetRepository(); + commit = repository.Head.Tip.Sha; + return true; } /// public override bool TryGetKey(string value, out IssueKey key) { - value = value.Replace(separator, Path.DirectorySeparatorChar); - var split = NormalizePath(value).Split('/', '\\'); + value = value.Replace(FileIssueKeyProvider.separator, Path.DirectorySeparatorChar); + string[] split = this.NormalizePath(value).Split('/', '\\'); if (split.Length == 4) { - var year = split[0]; - var month = split[1]; - var day = split[2]; - var id = split[3]; + string year = split[0]; + string month = split[1]; + string day = split[2]; + string id = split[3]; - if (DateTime.TryParse($"{year}/{month}/{day}", out var time)) + if (DateTime.TryParse($"{year}/{month}/{day}", out DateTime time)) { - key = IssueKey.Create(value.Replace(Path.DirectorySeparatorChar, separator)); + key = IssueKey.Create(value.Replace(Path.DirectorySeparatorChar, FileIssueKeyProvider.separator)); return true; } } @@ -107,27 +78,58 @@ public override bool TryGetKey(string value, out IssueKey key) return false; } - /// - /// Tries to get the commit of the current branch - /// - /// - /// - public bool TryGetGitCommit(out string commit) + private IEnumerable FindAll(string directory) { - using IRepository repository = root.GetRepository(); - commit = repository.Head.Tip.Sha; - return true; + List keys = new List(); + + if (!Directory.Exists(this.root.IssuesPath)) + { + return keys; + } + + foreach (DirectoryInfo year in Directory.EnumerateDirectories(this.root.IssuesPath) + .Select(d => new DirectoryInfo(d))) + foreach (DirectoryInfo month in Directory.EnumerateDirectories(year.FullName) + .Select(d => new DirectoryInfo(d))) + foreach (DirectoryInfo day in Directory.EnumerateDirectories(month.FullName) + .Select(d => new DirectoryInfo(d))) + foreach (DirectoryInfo id in Directory.EnumerateDirectories(day.FullName) + .Select(d => new DirectoryInfo(d))) + { + string path = Path.Combine(year.Name, month.Name, day.Name, id.Name); + if (this.TryGetKey(path, out IssueKey key)) + { + keys.Add(key); + } + } + + return keys; + } + + private string GetUniqueId(int length) + { + using SHA256 sha = SHA256.Create(); + byte[] checksum = sha.ComputeHash(Guid.NewGuid().ToByteArray()); + string key = BitConverter.ToString(checksum) + .Replace("-", "") + .ToUpperInvariant() + .Substring(0, length); + return key; } private string NormalizePath(string path) { - if (Directory.Exists(Path.Combine(root.IssuesPath, path))) + if (Directory.Exists(Path.Combine(this.root.IssuesPath, path))) + { return path; + } - if (root.IssuesPath.Contains(Path.GetFullPath(path))) - return root.IssuesPath - .Remove(0, root.IssuesPath.Length) + if (this.root.IssuesPath.Contains(Path.GetFullPath(path))) + { + return this.root.IssuesPath + .Remove(0, this.root.IssuesPath.Length) .Trim('/', '\\'); + } return string.Empty; } diff --git a/src/GitIssue/Issues/File/FileValueField.cs b/src/GitIssue/Issues/File/FileValueField.cs index bb3a00f..22a37bd 100644 --- a/src/GitIssue/Issues/File/FileValueField.cs +++ b/src/GitIssue/Issues/File/FileValueField.cs @@ -23,9 +23,9 @@ public class FileValueField : ValueField, IJsonField /// the issue root /// the field key public FileValueField(IssueRoot root, FieldKey key) - : base(key, default!) + : base(key, default(T)!) { - issueRoot = root; + this.issueRoot = root; } /// @@ -37,63 +37,67 @@ public FileValueField(IssueRoot root, FieldKey key) public FileValueField(IssueRoot root, FieldKey key, T value) : base(key, value) { - issueRoot = root; + this.issueRoot = root; } /// /// Gets the file used to save the fields value /// - public string FilePath => Path.Combine(issueRoot.IssuePath, Key); + public string FilePath => Path.Combine(this.issueRoot.IssuePath, this.Key); - /// - public override async Task SaveAsync() + /// + /// Asynchronously reads the field from disk + /// + /// the issue root + /// the field key + /// the field info + /// A task that represents the asynchronous read operation + public static async Task ReadAsync(IssueRoot issueRoot, FieldKey key, FieldInfo info) { try { - await using var stream = new FileStream(FilePath, FileMode.Create, FileAccess.ReadWrite); - await using var writer = new StreamWriter(stream); - await writer.WriteAsync(Value?.ToString()); - return true; + string fieldFile = Path.Combine(issueRoot.IssuePath, key.ToString()); + string content = await System.IO.File.ReadAllTextAsync(fieldFile); + FileValueField field = new FileValueField(issueRoot, key); + if (field.TryParse(content, out T value)) + { + field.Value = value; + } + + throw new SerializationException($"Unable to convert field content to type {typeof(T)}"); } catch (Exception e) { - throw new ArgumentException($"Failed to save field {Key} on issue {issueRoot.Key}", e); + throw new ArgumentException($"Failed to read field {key} on issue {issueRoot.Key}", e); } } /// - public override Task ExportAsync() + public override async Task SaveAsync() { - throw new NotImplementedException(); + try + { + await using FileStream stream = new FileStream(this.FilePath, FileMode.Create, FileAccess.ReadWrite); + await using StreamWriter writer = new StreamWriter(stream); + await writer.WriteAsync(this.Value?.ToString()); + return true; + } + catch (Exception e) + { + throw new ArgumentException($"Failed to save field {this.Key} on issue {this.issueRoot.Key}", e); + } } /// public JToken ToJson() { - return new JValue(Value); + return new JValue(this.Value); } - /// - /// Asynchronously reads the field from disk - /// - /// the issue root - /// the field key - /// the field info - /// A task that represents the asynchronous read operation - public static async Task ReadAsync(IssueRoot issueRoot, FieldKey key, FieldInfo info) + /// + public override Task ExportAsync() { - try - { - var fieldFile = Path.Combine(issueRoot.IssuePath, key.ToString()); - var content = await System.IO.File.ReadAllTextAsync(fieldFile); - var field = new FileValueField(issueRoot, key); - if (field.TryParse(content, out var value)) field.Value = value; - throw new SerializationException($"Unable to convert field content to type {typeof(T)}"); - } - catch (Exception e) - { - throw new ArgumentException($"Failed to read field {key} on issue {issueRoot.Key}", e); - } + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/IIssue.cs b/src/GitIssue/Issues/IIssue.cs index 0d4a10e..79c002d 100644 --- a/src/GitIssue/Issues/IIssue.cs +++ b/src/GitIssue/Issues/IIssue.cs @@ -13,24 +13,24 @@ namespace GitIssue.Issues public interface IIssue : IReadOnlyIssue { /// - /// Gets or sets the issue title + /// Gets or sets the Author /// - new String Title { get; set; } + new Signature Author { get; set; } /// - /// Gets or sets the issue description + /// Gets or sets when the issue was created /// - new String Description { get; set; } + new DateTime Created { get; set; } /// - /// Gets or sets the Author + /// Gets or sets the issue description /// - new Signature Author { get; set; } + new String Description { get; set; } /// - /// Gets or sets when the issue was created + /// Gets or sets the issue title /// - new DateTime Created { get; set; } + new String Title { get; set; } /// /// Gets or sets when the issue was last updates @@ -38,23 +38,23 @@ public interface IIssue : IReadOnlyIssue new DateTime Updated { get; set; } /// - /// Gets a for the provided key + /// Saves the issue /// - /// /// - public IFieldFactory SetField([CallerMemberName] string? key = null); + Task SaveAsync(); /// - /// Gets a for the provided + /// Gets a for the provided key /// /// /// - public IFieldFactory SetField(FieldKey key); + IFieldFactory SetField([CallerMemberName] string? key = null); /// - /// Saves the issue + /// Gets a for the provided /// + /// /// - Task SaveAsync(); + IFieldFactory SetField(FieldKey key); } } \ No newline at end of file diff --git a/src/GitIssue/Issues/IIssueKeyProvider.cs b/src/GitIssue/Issues/IIssueKeyProvider.cs index 0702e0a..9c601fb 100644 --- a/src/GitIssue/Issues/IIssueKeyProvider.cs +++ b/src/GitIssue/Issues/IIssueKeyProvider.cs @@ -13,24 +13,24 @@ public interface IIssueKeyProvider IEnumerable Keys { get; } /// - /// Tries to get the specified key from a string + /// Gets the path for the issue key /// - /// /// /// - bool TryGetKey(string value, out IssueKey key); + string GetIssuePath(IssueKey key); /// - /// Gets the path for the issue key + /// The next, unique, issue key /// - /// /// - string GetIssuePath(IssueKey key); + IssueKey Next(); /// - /// The next, unique, issue key + /// Tries to get the specified key from a string /// + /// + /// /// - IssueKey Next(); + bool TryGetKey(string value, out IssueKey key); } } \ No newline at end of file diff --git a/src/GitIssue/Issues/IReadOnlyIssue.cs b/src/GitIssue/Issues/IReadOnlyIssue.cs index 83eb423..50a4cbb 100644 --- a/src/GitIssue/Issues/IReadOnlyIssue.cs +++ b/src/GitIssue/Issues/IReadOnlyIssue.cs @@ -13,14 +13,14 @@ namespace GitIssue.Issues public interface IReadOnlyIssue : IReadOnlyDictionary { /// - /// Gets the Issue Key + /// Gets or sets when the issue was created /// - IssueKey Key { get; } + Signature Author { get; } /// - /// Gets or sets the issue title + /// Gets or sets when the issue was created /// - String Title { get; } + DateTime Created { get; } /// /// Gets or sets the issue description @@ -28,14 +28,14 @@ public interface IReadOnlyIssue : IReadOnlyDictionary String Description { get; } /// - /// Gets or sets when the issue was created + /// Gets the Issue Key /// - Signature Author { get; } + IssueKey Key { get; } /// - /// Gets or sets when the issue was created + /// Gets or sets the issue title /// - DateTime Created { get; } + String Title { get; } /// /// Gets or sets when the issue was last updates @@ -47,13 +47,13 @@ public interface IReadOnlyIssue : IReadOnlyDictionary /// /// /// - public IFieldProvider GetField([CallerMemberName] string? key = null); + IFieldProvider GetField([CallerMemberName] string? key = null); /// /// Gets a for the provided /// /// /// - public IFieldProvider GetField(FieldKey key); + IFieldProvider GetField(FieldKey key); } } \ No newline at end of file diff --git a/src/GitIssue/Issues/ITrackedIssue.cs b/src/GitIssue/Issues/ITrackedIssue.cs index 5754a10..2bec4b1 100644 --- a/src/GitIssue/Issues/ITrackedIssue.cs +++ b/src/GitIssue/Issues/ITrackedIssue.cs @@ -5,7 +5,7 @@ namespace GitIssue.Issues { /// - /// Interface for the tracked issue + /// Interface for the tracked issue /// public interface ITrackedIssue { diff --git a/src/GitIssue/Issues/Issue.cs b/src/GitIssue/Issues/Issue.cs index 319c98c..3c39750 100644 --- a/src/GitIssue/Issues/Issue.cs +++ b/src/GitIssue/Issues/Issue.cs @@ -28,106 +28,76 @@ protected Issue(IssueRoot root) this.Root = root; } - /// - /// Gets or sets the repository root - /// - public IssueRoot Root { get; } - - /// - public IssueKey Key - { - get => GetField().AsValue(); - protected set => SetField().WithValue(value); - } - /// - public String Title + public Signature Author { - get => GetField().AsValue(); - set => SetField().WithValue(value); + get => this.GetField().AsValue(); + set => this.SetField().WithValue(value); } /// - public String Description + public String[] Comments { - get => GetField().AsValue(); - set => SetField().WithValue(value); + get => this.GetField().AsArray(); + set => this.SetField().WithArray(value); } - /// - public Signature Author - { - get => GetField().AsValue(); - set => SetField().WithValue(value); - } + /// + public abstract int Count { get; } /// public DateTime Created { - get => GetField().AsValue(); - set => SetField().WithValue(value); + get => this.GetField().AsValue(); + set => this.SetField().WithValue(value); } /// - public DateTime Updated - { - get => GetField().AsValue(); - set => SetField().WithValue(value); - } - - /// - public String[] Comments + public String Description { - get => GetField().AsArray(); - set => SetField().WithArray(value); + get => this.GetField().AsValue(); + set => this.SetField().WithValue(value); } - /// - public abstract IFieldProvider GetField([CallerMemberName] string? key = null); - - /// - public abstract IFieldProvider GetField(FieldKey key); - - /// - public abstract IFieldFactory SetField([CallerMemberName] string? key = null); - - /// - public abstract IFieldFactory SetField(FieldKey key); - - /// - public abstract IEnumerable Keys { get; } - - /// - public abstract IEnumerable Values { get; } - - /// - public abstract int Count { get; } - /// public abstract IField this[FieldKey key] { get; } /// - public abstract Task SaveAsync(); + public IssueKey Key + { + get => this.GetField().AsValue(); + protected set => this.SetField().WithValue(value); + } /// - public abstract bool ContainsKey(FieldKey key); + public abstract IEnumerable Keys { get; } - /// - public abstract bool TryGetValue(FieldKey key, [MaybeNullWhen(false)] out IField value); + /// + /// Gets or sets the repository root + /// + public IssueRoot Root { get; } - /// - public abstract IEnumerator> GetEnumerator(); + /// + public String Title + { + get => this.GetField().AsValue(); + set => this.SetField().WithValue(value); + } - /// - IEnumerator IEnumerable.GetEnumerator() + /// + public DateTime Updated { - return GetEnumerator(); + get => this.GetField().AsValue(); + set => this.SetField().WithValue(value); } + /// + public abstract IEnumerable Values { get; } + /// public override string ToString() { - return $"{Key} {Title}"; + return $"{this.Key} {this.Title}"; } /// @@ -135,11 +105,12 @@ public override bool TryGetMember( GetMemberBinder binder, out object result) { FieldKey key = FieldKey.Create(binder.Name); - if (TryGetValue(key, out var field)) + if (this.TryGetValue(key, out IField? field)) { result = field; return true; } + result = null!; return true; } @@ -149,7 +120,7 @@ public override bool TrySetMember( SetMemberBinder binder, object? value) { FieldKey key = FieldKey.Create(binder.Name); - if (TryGetValue(key, out var field)) + if (this.TryGetValue(key, out IField? field)) { if (field is IValueField valueField) { @@ -180,7 +151,38 @@ public override bool TrySetMember( } } } + return false; } + + /// + public abstract IEnumerator> GetEnumerator(); + + /// + public abstract Task SaveAsync(); + + /// + public abstract IFieldFactory SetField([CallerMemberName] string? key = null); + + /// + public abstract IFieldFactory SetField(FieldKey key); + + /// + public abstract bool ContainsKey(FieldKey key); + + /// + public abstract bool TryGetValue(FieldKey key, [MaybeNullWhen(false)] out IField value); + + /// + public abstract IFieldProvider GetField([CallerMemberName] string? key = null); + + /// + public abstract IFieldProvider GetField(FieldKey key); + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/IssueKey.cs b/src/GitIssue/Issues/IssueKey.cs index def72d6..3bcde15 100644 --- a/src/GitIssue/Issues/IssueKey.cs +++ b/src/GitIssue/Issues/IssueKey.cs @@ -21,10 +21,14 @@ public struct IssueKey : IValue, IJsonValue, IEquatable /// the key string private IssueKey(string? key) { - if (key == null || key == nameof(None)) + if ((key == null) || (key == nameof(IssueKey.None))) + { this.key = string.Empty; + } else + { this.key = key; + } } /// @@ -53,7 +57,10 @@ public static IssueKey Create(string? key) { if (string.IsNullOrEmpty(x.key) && string.IsNullOrEmpty(y.key)) + { return true; + } + return x.key == y.key; } @@ -83,37 +90,37 @@ public static implicit operator string(IssueKey value) /// public static implicit operator IssueKey(string value) { - return Create(value); + return IssueKey.Create(value); } /// public override string ToString() { - return string.IsNullOrEmpty(key) ? nameof(None) : key; + return string.IsNullOrEmpty(this.key) ? nameof(IssueKey.None) : this.key; } /// public JToken ToJson() { - return new JValue(key); + return new JValue(this.key); } /// public override int GetHashCode() { - return string.IsNullOrEmpty(key) ? nameof(None).GetHashCode() : key.GetHashCode(); + return string.IsNullOrEmpty(this.key) ? nameof(IssueKey.None).GetHashCode() : this.key.GetHashCode(); } /// public override bool Equals(object? obj) { - return key.Equals(obj); + return this.key.Equals(obj); } /// public bool Equals(IssueKey other) { - return key == other.key; + return this.key == other.key; } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/IssueKeyTypeConverter.cs b/src/GitIssue/Issues/IssueKeyTypeConverter.cs index 477eb21..9b3588d 100644 --- a/src/GitIssue/Issues/IssueKeyTypeConverter.cs +++ b/src/GitIssue/Issues/IssueKeyTypeConverter.cs @@ -13,7 +13,11 @@ public class IssuedKeyTypeConverter : TypeConverter public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { - if (sourceType == typeof(string)) return true; + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); } @@ -21,7 +25,11 @@ public override bool CanConvertFrom(ITypeDescriptorContext? context, public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { - if (value is string str) return IssueKey.Create(str); + if (value is string str) + { + return IssueKey.Create(str); + } + return base.ConvertFrom(context, culture, value); } @@ -30,8 +38,13 @@ public override bool CanConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) + { if (value is IssueKey key) + { return key.ToString(); + } + } + return base.ConvertTo(context, culture, value, destinationType); } } diff --git a/src/GitIssue/Issues/IssueRoot.cs b/src/GitIssue/Issues/IssueRoot.cs index 2b63b4e..19e092e 100644 --- a/src/GitIssue/Issues/IssueRoot.cs +++ b/src/GitIssue/Issues/IssueRoot.cs @@ -18,8 +18,8 @@ public struct IssueRoot public IssueRoot(RepositoryRoot root, IssueKey key, string? issuePath) { this.issuePath = issuePath; - Root = root; - Key = key; + this.Root = root; + this.Key = key; } /// @@ -35,6 +35,6 @@ public IssueRoot(RepositoryRoot root, IssueKey key, string? issuePath) /// /// Gets the for the issue /// - public string IssuePath => Path.Combine(Root.IssuesPath, this.issuePath ?? Key.ToString()); + public string IssuePath => Path.Combine(this.Root.IssuesPath, this.issuePath ?? this.Key.ToString()); } } \ No newline at end of file diff --git a/src/GitIssue/Issues/Json/IJsonIssue.cs b/src/GitIssue/Issues/Json/IJsonIssue.cs index 981322a..5a4c0ee 100644 --- a/src/GitIssue/Issues/Json/IJsonIssue.cs +++ b/src/GitIssue/Issues/Json/IJsonIssue.cs @@ -10,7 +10,7 @@ public interface IJsonIssue : IIssue /// /// Gets the Json path for the issue /// - public string Json { get; } + string Json { get; } /// /// Converts the field to a JSON object for serialization diff --git a/src/GitIssue/Issues/Json/JsonArrayField.cs b/src/GitIssue/Issues/Json/JsonArrayField.cs index 3d67621..76313b1 100644 --- a/src/GitIssue/Issues/Json/JsonArrayField.cs +++ b/src/GitIssue/Issues/Json/JsonArrayField.cs @@ -40,17 +40,11 @@ public override Task SaveAsync() return Task.FromResult(true); } - /// - public override Task ExportAsync() - { - return Task.FromResult(ToString()); - } - /// public JToken ToJson() { - var array = new JArray(); - foreach (var value in Values) + JArray array = new JArray(); + foreach (T value in this.Values) { if (value is IJsonValue jValue) { @@ -63,5 +57,11 @@ public JToken ToJson() return array; } + + /// + public override Task ExportAsync() + { + return Task.FromResult(this.ToString()); + } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/Json/JsonFieldReader.cs b/src/GitIssue/Issues/Json/JsonFieldReader.cs index 8b678a8..85d1c90 100644 --- a/src/GitIssue/Issues/Json/JsonFieldReader.cs +++ b/src/GitIssue/Issues/Json/JsonFieldReader.cs @@ -14,24 +14,38 @@ public class JsonFieldReader : FieldReader /// public override bool CanCreateField(FieldInfo info) { - if (IsValueField(info) || IsArrayField(info)) + if (JsonFieldReader.IsValueField(info) || JsonFieldReader.IsArrayField(info)) + { return true; + } + return false; } /// public override bool CanReadField(FieldInfo info) { - if (IsValueField(info) || IsArrayField(info)) + if (JsonFieldReader.IsValueField(info) || JsonFieldReader.IsArrayField(info)) + { return true; + } + return false; } /// public override IField CreateField(Issue issue, FieldKey key, FieldInfo info) { - if (IsValueField(info)) return new JsonValueField(key); - if (IsArrayField(info)) return new JsonArrayField(key, new T[0]); + if (JsonFieldReader.IsValueField(info)) + { + return new JsonValueField(key); + } + + if (JsonFieldReader.IsArrayField(info)) + { + return new JsonArrayField(key, new T[0]); + } + return null!; } @@ -40,19 +54,23 @@ public override async Task ReadFieldAsync(Issue issue, FieldKey key, { if (issue is IJsonIssue jsonIssue) { - var json = await JsonIssueExtensions.ReadJsonFieldsAsync(jsonIssue.Json); - if (json.TryGetValue(key.ToString(), out var token)) + JObject json = await JsonIssueExtensions.ReadJsonFieldsAsync(jsonIssue.Json); + if (json.TryGetValue(key.ToString(), out JToken? token)) { - if (IsValueField(info) && token is JValue jValue) - if (TryGetValue(jValue, info, out T value)) + if (JsonFieldReader.IsValueField(info) && token is JValue jValue) + { + if (this.TryGetValue(jValue, info, out T value)) + { return new JsonValueField(key, value); + } + } - if (IsArrayField(info) && token is JArray jArray) + if (JsonFieldReader.IsArrayField(info) && token is JArray jArray) { - var values = jArray + T[] values = jArray .Select(t => t as JValue) .Where(t => t != null) - .Select(t => (s: TryGetValue(t!, info, out T v), r: v)) + .Select(t => (s: this.TryGetValue(t!, info, out T v), r: v)) .Where(t => t.s) .Select(t => t.r) .ToArray(); @@ -61,26 +79,29 @@ public override async Task ReadFieldAsync(Issue issue, FieldKey key, } } - return CreateField(issue, key, info); + return this.CreateField(issue, key, info); } return null!; } - private static bool IsValueField(FieldInfo info) + private static bool IsArrayField(FieldInfo info) { - return info.FieldType.Type == typeof(JsonValueField); + return info.FieldType.Type == typeof(JsonArrayField); } - private static bool IsArrayField(FieldInfo info) + private static bool IsValueField(FieldInfo info) { - return info.FieldType.Type == typeof(JsonArrayField); + return info.FieldType.Type == typeof(JsonValueField); } private bool TryGetValue(JValue jValue, FieldInfo info, out T value) { if (string.IsNullOrEmpty(info.ValueMetadata)) + { return ValueExtensions.TryParse(jValue.Value, out value); + } + return ValueExtensions.TryParse(jValue.Value, out value, info.ValueMetadata); } } diff --git a/src/GitIssue/Issues/Json/JsonIssue.cs b/src/GitIssue/Issues/Json/JsonIssue.cs index 70d828f..d352f66 100644 --- a/src/GitIssue/Issues/Json/JsonIssue.cs +++ b/src/GitIssue/Issues/Json/JsonIssue.cs @@ -37,9 +37,9 @@ public class JsonIssue : Issue, IJsonIssue /// the issue root public JsonIssue(IssueRoot root) : base(root) { - fields = new Dictionary(); - modifiedFields = new HashSet(); - keyProvider = new FileFieldKeyProvider(); + this.fields = new Dictionary(); + this.modifiedFields = new HashSet(); + this.keyProvider = new FileFieldKeyProvider(); this.Key = root.Key; } @@ -53,167 +53,188 @@ public JsonIssue(IssueRoot root, IDictionary fields) : { this.fields = fields.ToDictionary(f => f.Key, f => f.Value.CreateField(this, f.Key)); - modifiedFields = new HashSet(); - keyProvider = new FileFieldKeyProvider(); + this.modifiedFields = new HashSet(); + this.keyProvider = new FileFieldKeyProvider(); this.Key = root.Key; } + /// + public override int Count => this.fields.Count; + + /// + public override IField this[FieldKey key] => this.fields[key]; + /// /// Gets the Json path for the issue /// - public string Json => GetJsonFile(Root); + public string Json => JsonIssue.GetJsonFile(this.Root); /// - public override IEnumerable Keys => fields.Keys; + public override IEnumerable Keys => this.fields.Keys; /// - public override IEnumerable Values => fields.Values; + public override IEnumerable Values => this.fields.Values; - /// - public override int Count => fields.Count; + /// + /// Deletes an issue and all it's fields from disk. + /// + /// + /// + public static Task DeleteAsync(IssueRoot issueRoot) + { + // Issue should exist before attempting to delete + if (!Directory.Exists(issueRoot.IssuePath)) + { + throw new IssueNotFoundException($"The issue path {issueRoot.IssuePath} does not exist"); + } - /// - public override IField this[FieldKey key] => fields[key]; + // Delete the json file if it exists + if (System.IO.File.Exists(JsonIssue.GetJsonFile(issueRoot))) + { + System.IO.File.Delete(JsonIssue.GetJsonFile(issueRoot)); + } - /// - public override IFieldProvider GetField(string? key = null) - { - return GetField(keyProvider.FromString(key)); + // Delete the directory if empty + if (!Directory.GetFileSystemEntries(issueRoot.IssuePath).Any()) + { + Directory.Delete(issueRoot.IssuePath); + } + + // Success + return Task.FromResult(true); } - /// - public override IFieldFactory SetField(string? key = null) + /// + /// Gets the JSON file name + /// + /// + /// + public static string GetJsonFile(IssueRoot root) { - return SetField(keyProvider.FromString(key)); + return Path.Combine(root.IssuePath, $"{Path.GetFileName(root.Key.ToString())}.json"); } - /// - public override IFieldProvider GetField(FieldKey key) + /// + /// Reads an issue from disk + /// + /// the issue root + /// the expected fields + /// + public static async Task ReadAsync(IssueRoot root, + IDictionary fields) { - return new FieldProvider(this, key, () => + if (!Directory.Exists(root.IssuePath)) { - fields.TryGetValue(key, out var field); - return field; - }); + return null; + } + + JsonIssue issue = new JsonIssue(root, fields); + foreach (FieldKey key in fields.Keys) + { + IField valueField = await fields[key].ReadFieldAsync(issue, key); + issue.fields[key] = valueField; + } + + return issue; } /// - public override IFieldFactory SetField(FieldKey key) + public override IEnumerator> GetEnumerator() { - return new FieldFactory(this, key, () => + foreach (KeyValuePair kvp in this.fields) { - fields.TryGetValue(key, out var field); - return field; - }); + yield return new KeyValuePair(kvp.Key, kvp.Value); + } } /// public override async Task SaveAsync() { // Make sure the issue root exists - if (Directory.Exists(Root.IssuePath) == false) - Directory.CreateDirectory(Root.IssuePath); + if (!Directory.Exists(this.Root.IssuePath)) + { + Directory.CreateDirectory(this.Root.IssuePath); + } // Set the created and updated dates - if (Created == DateTime.MinValue) - Created = DateTime.Now; - Updated = DateTime.Now; + if (this.Created == DateTime.MinValue) + { + this.Created = DateTime.Now; + } + + this.Updated = DateTime.Now; // Save as Json - await this.SaveAsJsonAsync(Json); + await this.SaveAsJsonAsync(this.Json); // Success return true; } /// - public override bool ContainsKey(FieldKey key) + public override IFieldFactory SetField(string? key = null) { - return fields.ContainsKey(key); + return this.SetField(this.keyProvider.FromString(key)); } /// - public override bool TryGetValue(FieldKey key, [MaybeNullWhen(false)] out IField value) + public override IFieldFactory SetField(FieldKey key) { - if (fields.TryGetValue(key, out var field)) + return new FieldFactory(this, key, () => { - value = field; - return true; - } - - value = null!; - return false; - } - - /// - public override IEnumerator> GetEnumerator() - { - foreach (var kvp in fields) yield return new KeyValuePair(kvp.Key, kvp.Value); + this.fields.TryGetValue(key, out IField? field); + return field; + }); } /// public JObject ToJson() { - var json = new JObject(); - foreach (var kvp in fields) + JObject json = new JObject(); + foreach (KeyValuePair kvp in this.fields) + { if (kvp.Value is IJsonField field) + { json[kvp.Key.ToString()] = field.ToJson(); + } + } + return json; } - /// - /// Gets the JSON file name - /// - /// - /// - public static string GetJsonFile(IssueRoot root) + /// + public override bool ContainsKey(FieldKey key) { - return Path.Combine(root.IssuePath, $"{Path.GetFileName(root.Key.ToString())}.json"); + return this.fields.ContainsKey(key); } - /// - /// Reads an issue from disk - /// - /// the issue root - /// the expected fields - /// - public static async Task ReadAsync(IssueRoot root, - IDictionary fields) + /// + public override bool TryGetValue(FieldKey key, [MaybeNullWhen(false)] out IField value) { - if (Directory.Exists(root.IssuePath) == false) - return null; - - var issue = new JsonIssue(root, fields); - foreach (var key in fields.Keys) + if (this.fields.TryGetValue(key, out IField? field)) { - var valueField = await fields[key].ReadFieldAsync(issue, key); - issue.fields[key] = valueField; + value = field; + return true; } - return issue; + value = null!; + return false; } - /// - /// Deletes an issue and all it's fields from disk. - /// - /// - /// - public static Task DeleteAsync(IssueRoot issueRoot) + /// + public override IFieldProvider GetField(string? key = null) { - // Issue should exist before attempting to delete - if (Directory.Exists(issueRoot.IssuePath) == false) - throw new IssueNotFoundException($"The issue path {issueRoot.IssuePath} does not exist"); - - // Delete the json file if it exists - if (System.IO.File.Exists(GetJsonFile(issueRoot))) - System.IO.File.Delete(GetJsonFile(issueRoot)); - - // Delete the directory if empty - if (!Directory.GetFileSystemEntries(issueRoot.IssuePath).Any()) - Directory.Delete(issueRoot.IssuePath); + return this.GetField(this.keyProvider.FromString(key)); + } - // Success - return Task.FromResult(true); + /// + public override IFieldProvider GetField(FieldKey key) + { + return new FieldProvider(this, key, () => + { + this.fields.TryGetValue(key, out IField? field); + return field; + }); } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/Json/JsonIssueExtensions.cs b/src/GitIssue/Issues/Json/JsonIssueExtensions.cs index bbc0ba7..c53962f 100644 --- a/src/GitIssue/Issues/Json/JsonIssueExtensions.cs +++ b/src/GitIssue/Issues/Json/JsonIssueExtensions.cs @@ -13,32 +13,32 @@ public static class JsonIssueExtensions /// /// Saves the issue as Json /// - /// /// /// - public static async Task SaveAsJsonAsync(this IJsonIssue issue, string path) + public static async Task ReadJsonFieldsAsync(string path) { - // Save the json - await using var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite); - using JsonWriter writer = new JsonTextWriter(new StreamWriter(stream)); - var serializer = new JsonSerializer(); - serializer.Formatting = Formatting.Indented; - serializer.Serialize(writer, issue.ToJson()); + // Read the json + await using FileStream stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read); + using JsonReader reader = new JsonTextReader(new StreamReader(stream)); + JsonSerializer serializer = new JsonSerializer(); + JObject json = (JObject)serializer.Deserialize(reader)!; + return json; } /// /// Saves the issue as Json /// + /// /// /// - public static async Task ReadJsonFieldsAsync(string path) + public static async Task SaveAsJsonAsync(this IJsonIssue issue, string path) { - // Read the json - await using var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read); - using JsonReader reader = new JsonTextReader(new StreamReader(stream)); - var serializer = new JsonSerializer(); - var json = (JObject)serializer.Deserialize(reader)!; - return json; + // Save the json + await using FileStream stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite); + using JsonWriter writer = new JsonTextWriter(new StreamWriter(stream)); + JsonSerializer serializer = new JsonSerializer(); + serializer.Formatting = Formatting.Indented; + serializer.Serialize(writer, issue.ToJson()); } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/Json/JsonIssueManagerExtensions.cs b/src/GitIssue/Issues/Json/JsonIssueManagerExtensions.cs index 5cbd89d..e65403f 100644 --- a/src/GitIssue/Issues/Json/JsonIssueManagerExtensions.cs +++ b/src/GitIssue/Issues/Json/JsonIssueManagerExtensions.cs @@ -21,7 +21,7 @@ public static class JsonIssueManagerExtensions /// public static async Task ExportAsJsonAsync(this IIssueManager manager, string path) { - await ExportAsJsonAsync(manager, path, AllIssues); + await manager.ExportAsJsonAsync(path, JsonIssueManagerExtensions.AllIssues); } @@ -36,13 +36,18 @@ public static async Task ExportAsJsonAsync(this IIssueManager manager, string pa Func predicate) { await using Stream stream = System.IO.File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite); - await using var writer = new StreamWriter(stream); - using var text = new JsonTextWriter(writer); + await using StreamWriter writer = new StreamWriter(stream); + using JsonTextWriter text = new JsonTextWriter(writer); { - var json = new JObject(); - await foreach (var issue in manager.FindAsync(predicate)) + JObject json = new JObject(); + await foreach (IIssue issue in manager.FindAsync(predicate)) + { if (issue is IJsonIssue jsonIssue) + { json[issue.Key.ToString()] = jsonIssue.ToJson(); + } + } + text.Formatting = Formatting.Indented; await json.WriteToAsync(text); } diff --git a/src/GitIssue/Issues/Json/JsonValueField.cs b/src/GitIssue/Issues/Json/JsonValueField.cs index cd61f8e..bae7c94 100644 --- a/src/GitIssue/Issues/Json/JsonValueField.cs +++ b/src/GitIssue/Issues/Json/JsonValueField.cs @@ -29,7 +29,7 @@ public class JsonValueField : ValueField, IJsonField /// Initialized a new instance of the class /// /// the field key - public JsonValueField(FieldKey key) : base(key, default!) + public JsonValueField(FieldKey key) : base(key, default(T)!) { } @@ -49,17 +49,20 @@ public override Task SaveAsync() } /// - public override Task ExportAsync() + public JToken ToJson() { - return Task.FromResult(ToString()); + if (this.Value is IJsonValue value) + { + return value.ToJson(); + } + + return new JValue(this.Value); } /// - public JToken ToJson() + public override Task ExportAsync() { - if (Value is IJsonValue value) - return value.ToJson(); - return new JValue(Value); + return Task.FromResult(this.ToString()); } } } \ No newline at end of file diff --git a/src/GitIssue/Issues/TrackedIssue.cs b/src/GitIssue/Issues/TrackedIssue.cs index 0ad4cbf..c7493b6 100644 --- a/src/GitIssue/Issues/TrackedIssue.cs +++ b/src/GitIssue/Issues/TrackedIssue.cs @@ -25,10 +25,15 @@ public TrackedIssue() /// the issue key to track public TrackedIssue(IssueKey key) { - Key = key; - Started = DateTime.Now; + this.Key = key; + this.Started = DateTime.Now; } + /// + /// Gets the none tracked issue + /// + public static TrackedIssue None => new TrackedIssue(); + /// /// Gets the key of the tracked issue /// @@ -41,11 +46,6 @@ public TrackedIssue(IssueKey key) [JsonProperty] public DateTime Started { get; set; } = DateTime.MinValue; - /// - /// Gets the none tracked issue - /// - public static TrackedIssue None => new TrackedIssue(); - /// /// Equals /// @@ -55,9 +55,15 @@ public TrackedIssue(IssueKey key) public static bool operator ==(TrackedIssue lhs, TrackedIssue rhs) { if (string.IsNullOrEmpty(lhs?.Key) && string.IsNullOrEmpty(rhs?.Key)) + { return true; + } + if (string.IsNullOrEmpty(lhs?.Key) || string.IsNullOrEmpty(rhs?.Key)) + { return false; + } + return rhs.Equals(lhs); } @@ -72,15 +78,45 @@ public TrackedIssue(IssueKey key) return !(lhs == rhs); } + /// + /// Reads the configuration from a file + /// + /// the tracking file + /// the logger + /// the + public static TrackedIssue Read(string file, ILogger? logger = null) + { + try + { + if (System.IO.File.Exists(file)) + { + using FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read); + using StreamReader reader = new StreamReader(stream); + JsonSerializer serializer = new JsonSerializer(); + TrackedIssue tracked = (TrackedIssue)serializer.Deserialize(reader, typeof(TrackedIssue))!; + return tracked; + } + } + catch (Exception ex) + { + logger?.Error($"Unable to deserialize {file} as tracked issue file ", ex); + } + + return TrackedIssue.None; + } + /// public override bool Equals(object? obj) { if (obj is TrackedIssue track) { - if (string.IsNullOrEmpty(Key) && + if (string.IsNullOrEmpty(this.Key) && string.IsNullOrEmpty(track.Key)) + { return true; - return Key == track.Key; + } + + return this.Key == track.Key; } return base.Equals(obj); @@ -89,7 +125,7 @@ public override bool Equals(object? obj) /// public override int GetHashCode() { - return Key.GetHashCode(); + return this.Key.GetHashCode(); } /// @@ -101,13 +137,9 @@ public async Task SaveAsync(string file, ILogger? logger = null) { try { - await using var stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); - await using var writer = new StreamWriter(stream); - var serializer = JsonSerializer.Create(new JsonSerializerSettings - { - Formatting = Formatting.Indented, - DefaultValueHandling = DefaultValueHandling.Ignore - }); + await using FileStream stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite); + await using StreamWriter writer = new StreamWriter(stream); + JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings { Formatting = Formatting.Indented, DefaultValueHandling = DefaultValueHandling.Ignore }); serializer.Serialize(writer, this, typeof(TrackedIssue)); } catch (Exception ex) @@ -115,32 +147,5 @@ public async Task SaveAsync(string file, ILogger? logger = null) logger?.Error($"Unable to serialize {file} as tracked issue file ", ex); } } - - /// - /// Reads the configuration from a file - /// - /// the tracking file - /// the logger - /// the - public static TrackedIssue Read(string file, ILogger? logger = null) - { - try - { - if (System.IO.File.Exists(file)) - { - using var stream = new FileStream(file, FileMode.Open, FileAccess.Read); - using var reader = new StreamReader(stream); - var serializer = new JsonSerializer(); - var tracked = (TrackedIssue)serializer.Deserialize(reader, typeof(TrackedIssue))!; - return tracked; - } - } - catch (Exception ex) - { - logger?.Error($"Unable to deserialize {file} as tracked issue file ", ex); - } - - return None; - } } } \ No newline at end of file diff --git a/src/GitIssue/Paths.cs b/src/GitIssue/Paths.cs index f14b6cf..b63eab0 100644 --- a/src/GitIssue/Paths.cs +++ b/src/GitIssue/Paths.cs @@ -6,27 +6,27 @@ public static class Paths { /// - /// Gets or sets the name of the git folder + /// Gets or sets the change log file name /// - public static string GitFolderName { get; set; } = ".git"; + public static string ChangeLogFileName { get; set; } = "changes.json"; /// - /// Gets or sets the name of the issues folder + /// Gets or sets the config file name /// - public static string IssueRootFolderName { get; set; } = ".issues"; + public static string ConfigFileName { get; set; } = "config.json"; /// - /// Gets or sets the config file name + /// Gets or sets the name of the git folder /// - public static string ConfigFileName { get; set; } = "config.json"; + public static string GitFolderName { get; set; } = ".git"; /// - /// Gets or sets the change log file name + /// Gets or sets the name of the issues folder /// - public static string ChangeLogFileName { get; set; } = "changes.json"; + public static string IssueRootFolderName { get; set; } = ".issues"; /// - /// Gets or sets the tracked issue file name + /// Gets or sets the tracked issue file name /// public static string TrackedIssueFileName { get; set; } = "tracking.json"; } diff --git a/src/GitIssue/RepositoryRoot.cs b/src/GitIssue/RepositoryRoot.cs index 8fd91f9..d9cfc16 100644 --- a/src/GitIssue/RepositoryRoot.cs +++ b/src/GitIssue/RepositoryRoot.cs @@ -23,9 +23,7 @@ public struct RepositoryRoot /// /// Gets the issue path /// - public string IssuesPath => this.Name == null ? - this.RootPath : - Path.Combine(this.RootPath, Name); + public string IssuesPath => this.Name == null ? this.RootPath : Path.Combine(this.RootPath, this.Name); /// /// Gets the repository name @@ -35,22 +33,22 @@ public struct RepositoryRoot /// /// Gets the path for the config file /// - public string ConfigFile => Path.Combine(IssuesPath, Paths.ConfigFileName); + public string ConfigFile => Path.Combine(this.IssuesPath, Paths.ConfigFileName); /// /// Gets the path to the change log /// - public string ChangeLog => Path.Combine(IssuesPath, Paths.ChangeLogFileName); + public string ChangeLog => Path.Combine(this.IssuesPath, Paths.ChangeLogFileName); /// /// Gets the path to the tracked file /// - public string Tracked => Path.Combine(IssuesPath, Paths.TrackedIssueFileName); + public string Tracked => Path.Combine(this.IssuesPath, Paths.TrackedIssueFileName); /// /// Gets the issue configuration /// - public IssueConfiguration Configuration => IssueConfiguration.Read(ConfigFile); + public IssueConfiguration Configuration => IssueConfiguration.Read(this.ConfigFile); /// /// Initializes a new instance of the struct @@ -58,8 +56,8 @@ public struct RepositoryRoot /// the directory of the repository internal RepositoryRoot(string directory) { - RootPath = directory; - Name = null; + this.RootPath = directory; + this.Name = null; } /// @@ -69,8 +67,8 @@ internal RepositoryRoot(string directory) /// the name of the repository internal RepositoryRoot(string directory, string name) { - RootPath = directory; - Name = name; + this.RootPath = directory; + this.Name = name; } /// @@ -81,8 +79,8 @@ internal RepositoryRoot(string directory, string name) /// the repository root public static RepositoryRoot Create(string directory, string name) { - var info = new DirectoryInfo(directory); - var created = info.CreateSubdirectory(name).FullName; + DirectoryInfo info = new DirectoryInfo(directory); + string created = info.CreateSubdirectory(name).FullName; return new RepositoryRoot(directory, name); } @@ -94,13 +92,17 @@ public static RepositoryRoot Create(string directory, string name) /// the repository root public static RepositoryRoot Open(string directory, string? name) { - var current = new DirectoryInfo(directory); + DirectoryInfo current = new DirectoryInfo(directory); if (name == null) + { return new RepositoryRoot(current.FullName); + } - if (IsIssueRoot(current, name, out var issues)) + if (RepositoryRoot.IsIssueRoot(current, name, out DirectoryInfo issues)) + { return new RepositoryRoot(current.FullName, name); + } throw new RepositoryNotFoundException($"Failed to open directory {directory} as issue root"); } @@ -113,14 +115,18 @@ public static RepositoryRoot Open(string directory, string? name) /// the repository root public static RepositoryRoot Locate(string directory, string name) { - var current = new DirectoryInfo(directory); - while (TryGetParent(current, out var parent)) + DirectoryInfo current = new DirectoryInfo(directory); + while (RepositoryRoot.TryGetParent(current, out DirectoryInfo parent)) { - if (IsIssueRoot(current, name, out var issues)) + if (RepositoryRoot.IsIssueRoot(current, name, out DirectoryInfo issues)) + { return new RepositoryRoot(current.FullName, name); + } + current = parent; } - return None; + + return RepositoryRoot.None; } /// @@ -151,11 +157,11 @@ public static bool IsIssueRoot(DirectoryInfo directory, string name, out Directo private static bool TryGetParent(DirectoryInfo directory, out DirectoryInfo parent) { parent = directory.Parent!; - return directory.Exists && directory.Parent != null; + return directory.Exists && (directory.Parent != null); } /// - /// Gets the GIT repository at the root + /// Gets the GIT repository at the root /// /// [Pure] diff --git a/src/GitIssue/SafeResult.cs b/src/GitIssue/SafeResult.cs index 9291b64..714a814 100644 --- a/src/GitIssue/SafeResult.cs +++ b/src/GitIssue/SafeResult.cs @@ -13,7 +13,7 @@ public class SafeResult /// the success of the task public SafeResult(bool success = false) { - IsSuccess = success; + this.IsSuccess = success; } /// @@ -22,27 +22,18 @@ public SafeResult(bool success = false) /// the exception thrown public SafeResult(Exception exception) : this() { - Exception = exception; + this.Exception = exception; } - /// - /// Returns true if the command is a success - /// - public bool IsSuccess { get; set; } - /// /// Captures the exception if failed /// public Exception? Exception { get; set; } /// - /// Returns a successful command + /// Returns true if the command is a success /// - /// - public static SafeResult Success() - { - return new SafeResult(true); - } + public bool IsSuccess { get; set; } /// /// Returns a failed command @@ -62,6 +53,15 @@ public static SafeResult Fail(Exception e) { return new SafeResult(e); } + + /// + /// Returns a successful command + /// + /// + public static SafeResult Success() + { + return new SafeResult(true); + } } /// @@ -75,7 +75,7 @@ public class SafeResult : SafeResult /// public SafeResult(Exception exception) : base(exception) { - Result = default!; + this.Result = default(T?)!; } /// @@ -83,7 +83,7 @@ public SafeResult(Exception exception) : base(exception) /// public SafeResult(bool success = false) : base(success) { - Result = default!; + this.Result = default(T?)!; } /// @@ -92,7 +92,7 @@ public SafeResult(bool success = false) : base(success) /// public SafeResult(T result) : base(true) { - Result = result; + this.Result = result; } /// @@ -130,38 +130,45 @@ public static SafeResult Success(T result) } /// - /// Checks it a result was returned from the task + /// Gets the result of the command /// - /// + /// throw a if the task failed /// - public bool HasResult(out T result) + public T GetResult(bool throwIfFailed = true) { - if (IsSuccess) + if (this.IsSuccess) { - result = Result; - return true; + return this.Result; } - result = default!; - return false; + if (!throwIfFailed) + { + return default(T)!; + } + + if (this.Exception == null) + { + throw new SafeResultException("Task execution failed"); + } + + throw new SafeResultException("Task execution failed", this.Exception); } /// - /// Gets the result of the command + /// Checks it a result was returned from the task /// - /// throw a if the task failed + /// /// - public T GetResult(bool throwIfFailed = true) + public bool HasResult(out T result) { if (this.IsSuccess) - return this.Result; - - if (throwIfFailed == false) - return default!; + { + result = this.Result; + return true; + } - if (this.Exception == null) - throw new SafeResultException("Task execution failed"); - throw new SafeResultException("Task execution failed", this.Exception); + result = default(T)!; + return false; } } } \ No newline at end of file diff --git a/src/GitIssue/Syncs/FileImporter.cs b/src/GitIssue/Syncs/FileImporter.cs index 5c261d0..f9b9d70 100644 --- a/src/GitIssue/Syncs/FileImporter.cs +++ b/src/GitIssue/Syncs/FileImporter.cs @@ -5,33 +5,34 @@ namespace GitIssue.Syncs { /// - /// Imports issues from a JSON description + /// Imports issues from a JSON description /// public class FileImporter : Importer { /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// /// public FileImporter(IIssueManager manager) : base(manager) { - } /// - /// Gets or sets the file to import + /// Gets or sets the file to import /// public string ImportPath { get; set; } = "import.json"; /// public override async Task Import() { - if (File.Exists(this.ImportPath) == false) + if (!File.Exists(this.ImportPath)) + { return false; + } - var content = await File.ReadAllTextAsync(this.ImportPath); - var json = JObject.Parse(content); - return await Import(json); + string content = await File.ReadAllTextAsync(this.ImportPath); + JObject json = JObject.Parse(content); + return await this.Import(json); } } -} +} \ No newline at end of file diff --git a/src/GitIssue/Syncs/IImporter.cs b/src/GitIssue/Syncs/IImporter.cs index a45dd90..e5403e7 100644 --- a/src/GitIssue/Syncs/IImporter.cs +++ b/src/GitIssue/Syncs/IImporter.cs @@ -4,19 +4,19 @@ namespace GitIssue.Syncs { /// - /// Importing interface + /// Importing interface /// public interface IImporter : IAsyncEnumerable { /// - /// Gets the Root + /// Gets the Root /// - public SyncRoot Root { get; } + SyncRoot Root { get; } /// - /// Imports the issues + /// Imports the issues /// /// - public Task Import(); + Task Import(); } } \ No newline at end of file diff --git a/src/GitIssue/Syncs/Importer.cs b/src/GitIssue/Syncs/Importer.cs index e702a37..077606c 100644 --- a/src/GitIssue/Syncs/Importer.cs +++ b/src/GitIssue/Syncs/Importer.cs @@ -12,14 +12,14 @@ namespace GitIssue.Syncs { /// - /// Imports issues from a JSON description + /// Imports issues from a JSON description /// public abstract class Importer : IImporter { private readonly IIssueManager manager; /// - /// Initializes an instance of the class + /// Initializes an instance of the class /// /// public Importer(IIssueManager manager) @@ -30,62 +30,67 @@ public Importer(IIssueManager manager) } /// - /// Gets the base path for imported issues + /// The configuration of the sync /// - public SyncRoot Root { get; set; } + public SyncConfiguration Configuration { get; set; } /// - /// The configuration of the sync + /// Gets the base path for imported issues /// - public SyncConfiguration Configuration { get; set; } - - /// - public abstract Task Import(); + public SyncRoot Root { get; set; } /// public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { - if (Directory.Exists(this.Root.ImportPath) == false) + if (!Directory.Exists(this.Root.ImportPath)) + { yield break; + } - var files = Directory + IEnumerable files = Directory .EnumerateFiles(this.Root.ImportPath, "*.import", SearchOption.AllDirectories); - foreach (var file in files) + foreach (string file in files) { cancellationToken.ThrowIfCancellationRequested(); yield return await SyncedIssue.ReadAsync(this, file); } } + /// + public abstract Task Import(); + /// - /// Imports an issue from a JSON object + /// Imports an issue from a JSON object /// /// /// protected async Task Import(JObject json) { - var tokens = new Dictionary(); - foreach (var kvp in this.Configuration.Mapping) + Dictionary tokens = new Dictionary(); + foreach (KeyValuePair kvp in this.Configuration.Mapping) { FieldKey key = FieldKey.Create(kvp.Key); - if (manager.Configuration.Fields.ContainsKey(key)) + if (this.manager.Configuration.Fields.ContainsKey(key)) { JToken? token = json.SelectToken(kvp.Value); - if (token != null) tokens[key] = token; + if (token != null) + { + tokens[key] = token; + } } } - return await Import(tokens); + return await this.Import(tokens); } private async Task Import(Dictionary tokens) { IssueKey importKey; - if (TryGetFieldValue(tokens, nameof(IIssue.Key), out var importKeyValue)) + if (this.TryGetFieldValue(tokens, nameof(IReadOnlyIssue.Key), out string importKeyValue)) { importKey = IssueKey.Create(importKeyValue); - tokens.Remove(FieldKey.Create(nameof(IIssue.Key))); + tokens.Remove(FieldKey.Create(nameof(IReadOnlyIssue.Key))); } else { @@ -93,7 +98,7 @@ private async Task Import(Dictionary tokens) } string title; - if (TryGetFieldValue(tokens, nameof(IIssue.Title), out var titleValue)) + if (this.TryGetFieldValue(tokens, nameof(IIssue.Title), out string titleValue)) { title = titleValue; } @@ -103,7 +108,7 @@ private async Task Import(Dictionary tokens) } string description; - if (TryGetFieldValue(tokens, nameof(IIssue.Description), out var descriptionValue)) + if (this.TryGetFieldValue(tokens, nameof(IIssue.Description), out string descriptionValue)) { description = descriptionValue; } @@ -112,24 +117,24 @@ private async Task Import(Dictionary tokens) description = string.Empty; } - var synced = await this + SyncedIssue synced = await this .FirstOrDefaultAsync(s => s.ImportKey == importKey); IIssue? issue = null; if (synced.IsValid) { - issue = await manager + issue = await this.manager .FindAsync(i => i.Key == importKey) .FirstOrDefaultAsync(); } if (issue == null) { - issue = await manager.CreateAsync(title, description); + issue = await this.manager.CreateAsync(title, description); synced = new SyncedIssue(this, issue.Key, importKey); } - if (await Import(issue, tokens)) + if (await this.Import(issue, tokens)) { await issue.SaveAsync(); await synced.SaveAsync(); @@ -141,15 +146,15 @@ private async Task Import(Dictionary tokens) private async Task Import(IIssue issue, Dictionary tokens) { - foreach (var token in tokens) + foreach (KeyValuePair token in tokens) { - if (issue.TryGetValue(token.Key, out var field)) + if (issue.TryGetValue(token.Key, out IField? field)) { if (token.Value is JValue value) { if (field is IValueField valueField) { - var converted = value.ToObject(valueField.ValueType); + object? converted = value.ToObject(valueField.ValueType); valueField.Value = converted; } } @@ -159,9 +164,9 @@ private async Task Import(IIssue issue, Dictionary token if (field is IArrayField arrayField) { arrayField.Clear(); - foreach (var item in array) + foreach (JToken item in array) { - var converted = item.ToObject(arrayField.ValueType); + object? converted = item.ToObject(arrayField.ValueType); arrayField.Add(converted); } } @@ -175,7 +180,7 @@ private async Task Import(IIssue issue, Dictionary token private bool TryGetFieldValue(Dictionary tokens, string field, out T result) { - if (tokens.TryGetValue(FieldKey.Create(field), out var token)) + if (tokens.TryGetValue(FieldKey.Create(field), out JToken? token)) { if (token is JValue value) { @@ -186,8 +191,9 @@ private bool TryGetFieldValue(Dictionary tokens, string fie } } } - result = default!; + + result = default(T)!; return false; } } -} +} \ No newline at end of file diff --git a/src/GitIssue/Syncs/SyncConfiguration.cs b/src/GitIssue/Syncs/SyncConfiguration.cs index 45cada1..8e0a86d 100644 --- a/src/GitIssue/Syncs/SyncConfiguration.cs +++ b/src/GitIssue/Syncs/SyncConfiguration.cs @@ -7,21 +7,20 @@ namespace GitIssue.Syncs { /// - /// Base class for importing issues + /// Base class for importing issues /// public class SyncConfiguration { /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// public SyncConfiguration() { - this.Mapping = new Dictionary(); } /// - /// Gets or sets the mapping between issue field and imported JSON + /// Gets or sets the mapping between issue field and imported JSON /// [JsonProperty] public Dictionary Mapping { get; set; } @@ -33,7 +32,7 @@ public SyncConfiguration() /// the public static Task ReadAsync(string file) { - return ReadAsync(file); + return SyncConfiguration.ReadAsync(file); } /// @@ -46,10 +45,10 @@ public static Task ReadAsync(string file) { try { - await using var stream = new FileStream(file, FileMode.Open, FileAccess.Read); - using var reader = new StreamReader(stream); - var serializer = new JsonSerializer(); - var configuration = (T)serializer.Deserialize(reader, typeof(T))!; + await using FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read); + using StreamReader reader = new StreamReader(stream); + JsonSerializer serializer = new JsonSerializer(); + T configuration = (T)serializer.Deserialize(reader, typeof(T))!; return configuration; } catch (Exception ex) @@ -58,5 +57,4 @@ public static Task ReadAsync(string file) } } } -} - +} \ No newline at end of file diff --git a/src/GitIssue/Syncs/SyncRoot.cs b/src/GitIssue/Syncs/SyncRoot.cs index ad79b02..cb40751 100644 --- a/src/GitIssue/Syncs/SyncRoot.cs +++ b/src/GitIssue/Syncs/SyncRoot.cs @@ -12,8 +12,8 @@ public struct SyncRoot /// public SyncRoot(RepositoryRoot root, string source) { - Root = root; - Source = source; + this.Root = root; + this.Source = source; } /// @@ -22,13 +22,13 @@ public SyncRoot(RepositoryRoot root, string source) public RepositoryRoot Root { get; } /// - /// Gets the source for the import + /// Gets the source for the import /// public string Source { get; } /// /// Gets the for the issue /// - public string ImportPath => Path.Combine(Root.IssuesPath, Source); + public string ImportPath => Path.Combine(this.Root.IssuesPath, this.Source); } } \ No newline at end of file diff --git a/src/GitIssue/Syncs/SyncedIssue.cs b/src/GitIssue/Syncs/SyncedIssue.cs index e1a0667..3adec3d 100644 --- a/src/GitIssue/Syncs/SyncedIssue.cs +++ b/src/GitIssue/Syncs/SyncedIssue.cs @@ -6,14 +6,14 @@ namespace GitIssue.Syncs { /// - /// Maps issues and imports + /// Maps issues and imports /// public struct SyncedIssue { private readonly IImporter importer; /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// /// /// @@ -25,7 +25,7 @@ public SyncedIssue(IImporter importer, KeyValuePair kvp) } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// /// /// @@ -38,7 +38,7 @@ public SyncedIssue(IImporter importer, IssueKey key, IssueKey import) } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// /// /// @@ -51,19 +51,19 @@ public SyncedIssue(IImporter importer, string key, string import) } /// - /// Gets the issue key + /// Gets the issue key /// public IssueKey IssueKey { get; } /// - /// Gets the import key + /// Gets the import key /// public IssueKey ImportKey { get; } /// - /// Gets a value indicating if this issue key is valid + /// Gets a value indicating if this issue key is valid /// - public bool IsValid => this.IssueKey != IssueKey.None || this.ImportKey != IssueKey.None; + public bool IsValid => (this.IssueKey != IssueKey.None) || (this.ImportKey != IssueKey.None); /// /// Implicit cast to string @@ -75,34 +75,38 @@ public static implicit operator KeyValuePair(SyncedIssue val } /// - /// Reads the imported mapping + /// Reads the imported mapping /// /// /// /// public static async Task ReadAsync(IImporter importer, string path) { - if (File.Exists(path) == false) - return default!; + if (!File.Exists(path)) + { + return default(SyncedIssue)!; + } - var import = Path.GetFileNameWithoutExtension(path); - var key = await File.ReadAllTextAsync(path); + string import = Path.GetFileNameWithoutExtension(path); + string key = await File.ReadAllTextAsync(path); return new SyncedIssue(importer, key, import); } /// - /// Saves the imported file + /// Saves the imported file /// /// public async Task SaveAsync() { - if (Directory.Exists(importer.Root.ImportPath) == false) - Directory.CreateDirectory(importer.Root.ImportPath); + if (!Directory.Exists(this.importer.Root.ImportPath)) + { + Directory.CreateDirectory(this.importer.Root.ImportPath); + } - var path = Path.Combine(importer.Root.ImportPath, this.ImportKey.ToString()); - var key = this.IssueKey.ToString(); + string path = Path.Combine(this.importer.Root.ImportPath, this.ImportKey.ToString()); + string key = this.IssueKey.ToString(); await File.WriteAllTextAsync(path, key); return true; } } -} +} \ No newline at end of file diff --git a/src/GitIssue/Values/DateTime.cs b/src/GitIssue/Values/DateTime.cs index ce64b64..ea5a29b 100644 --- a/src/GitIssue/Values/DateTime.cs +++ b/src/GitIssue/Values/DateTime.cs @@ -31,20 +31,20 @@ public static bool TryParse(string value, out DateTime datetime) internal DateTime(System.DateTime value) { this.value = value; - IsValid = true; + this.IsValid = true; } internal DateTime(string datetime) { try { - value = System.DateTime.Parse(datetime); - IsValid = true; + this.value = System.DateTime.Parse(datetime); + this.IsValid = true; } catch (FormatException) { - value = System.DateTime.MinValue; - IsValid = false; + this.value = System.DateTime.MinValue; + this.IsValid = false; } } @@ -56,7 +56,7 @@ internal DateTime(string datetime) /// public override string ToString() { - return value.ToString(CultureInfo.InvariantCulture); + return this.value.ToString(CultureInfo.InvariantCulture); } /// @@ -80,33 +80,36 @@ public static implicit operator DateTime(System.DateTime value) /// public JToken ToJson() { - return new JValue(value); + return new JValue(this.value); } /// public override bool Equals(object? obj) { if (obj is DateTime datetime) + { return this.Equals(datetime); + } + return base.Equals(obj); } /// public override int GetHashCode() { - return value.GetHashCode(); + return this.value.GetHashCode(); } /// public bool Equals([AllowNull] DateTime other) { - return value == other.value; + return this.value == other.value; } /// public bool Equals([AllowNull] System.DateTime other) { - return value == other; + return this.value == other; } /// diff --git a/src/GitIssue/Values/DateTimeTypeConverter.cs b/src/GitIssue/Values/DateTimeTypeConverter.cs index cd4155e..5f0574d 100644 --- a/src/GitIssue/Values/DateTimeTypeConverter.cs +++ b/src/GitIssue/Values/DateTimeTypeConverter.cs @@ -14,7 +14,7 @@ public override bool TryParse(string input, out DateTime result) /// public override bool TryParse(ValueMetadata input, out DateTime result) { - return TryParse(input.Value, out result); + return this.TryParse(input.Value, out result); } } } \ No newline at end of file diff --git a/src/GitIssue/Values/Email.cs b/src/GitIssue/Values/Email.cs index 5f53cf6..0f1db48 100644 --- a/src/GitIssue/Values/Email.cs +++ b/src/GitIssue/Values/Email.cs @@ -14,8 +14,6 @@ namespace GitIssue.Values [TypeAlias(nameof(Email))] public struct Email : IJsonValue, IEquatable, IValue { - private readonly string value; - /// /// Parses the value as an email address /// @@ -42,14 +40,14 @@ internal Email(string email) { try { - var parsed = new MailAddress(email); - value = parsed.ToString(); - IsValid = true; + MailAddress parsed = new MailAddress(email); + this.Item = parsed.ToString(); + this.IsValid = true; } catch (FormatException) { - value = string.Empty; - IsValid = false; + this.Item = string.Empty; + this.IsValid = false; } } @@ -61,42 +59,45 @@ internal Email(string email) /// public override string ToString() { - return value; + return this.Item; } /// public JToken ToJson() { - return new JValue(value); + return new JValue(this.Item); } /// public bool Equals(Email other) { - return value == other.value; + return this.Item == other.Item; } /// public override bool Equals(object? obj) { if (obj is Email email) + { return this.Equals(email); + } + return base.Equals(obj); } /// public override int GetHashCode() { - return value.GetHashCode(); + return this.Item.GetHashCode(); } /// public bool Equals([AllowNull] string other) { - return value == other; + return this.Item == other; } /// - public string Item => this.value; + public string Item { get; } } } \ No newline at end of file diff --git a/src/GitIssue/Values/EmailTypeConverter.cs b/src/GitIssue/Values/EmailTypeConverter.cs index ac5bf82..6f3047c 100644 --- a/src/GitIssue/Values/EmailTypeConverter.cs +++ b/src/GitIssue/Values/EmailTypeConverter.cs @@ -14,7 +14,7 @@ public override bool TryParse(string input, out Email result) /// public override bool TryParse(ValueMetadata input, out Email result) { - return TryParse(input.Value, out result); + return this.TryParse(input.Value, out result); } } } \ No newline at end of file diff --git a/src/GitIssue/Values/EnumTypeConverter.cs b/src/GitIssue/Values/EnumTypeConverter.cs index 34d2d2f..17bceef 100644 --- a/src/GitIssue/Values/EnumTypeConverter.cs +++ b/src/GitIssue/Values/EnumTypeConverter.cs @@ -8,7 +8,7 @@ public class EnumTypeConverter : ValueTypeConverter /// public override bool TryParse(string input, out Enumerated result) { - return TryParse(new ValueMetadata(input, string.Empty), out result); + return this.TryParse(new ValueMetadata(input, string.Empty), out result); } /// diff --git a/src/GitIssue/Values/Enumerated.cs b/src/GitIssue/Values/Enumerated.cs index b3220ba..efc4e8b 100644 --- a/src/GitIssue/Values/Enumerated.cs +++ b/src/GitIssue/Values/Enumerated.cs @@ -16,7 +16,6 @@ namespace GitIssue.Values public struct Enumerated : IJsonValue, IEquatable, IValue { private static readonly string regex = @"^\[(([\w]*)[,\s]*)*]$"; - private readonly string value; /// /// Tries to parse the enumerated value @@ -26,7 +25,7 @@ public struct Enumerated : IJsonValue, IEquatable, IValue /// public static bool TryParse(ValueMetadata meta, out Enumerated enumerated) { - if (TryParseMetadata(meta, out var values)) + if (Enumerated.TryParseMetadata(meta, out string[] values)) { if (values.Contains(meta.Value)) { @@ -34,17 +33,17 @@ public static bool TryParse(ValueMetadata meta, out Enumerated enumerated) return true; } - enumerated = default; + enumerated = default(Enumerated); return false; } - enumerated = default; + enumerated = default(Enumerated); return false; } private static bool TryParseMetadata(ValueMetadata metadata, out string[] values) { - var match = Regex.Match(metadata.Metadata, regex); + Match match = Regex.Match(metadata.Metadata, Enumerated.regex); if (match.Success) { values = match.Groups[2] @@ -56,7 +55,7 @@ private static bool TryParseMetadata(ValueMetadata metadata, out string[] values return true; } - values = default!; + values = default(string[])!; return false; } @@ -67,14 +66,14 @@ private static bool TryParseMetadata(ValueMetadata metadata, out string[] values /// public Enumerated(string value, string[] values) { - this.value = value; - Values = values; + this.Item = value; + this.Values = values; } /// /// Gets the enumerated value /// - public int Index => string.IsNullOrEmpty(value) ? 0 : Array.IndexOf(Values, value); + public int Index => string.IsNullOrEmpty(this.Item) ? 0 : Array.IndexOf(this.Values, this.Item); /// /// Gets the set of values @@ -82,44 +81,47 @@ public Enumerated(string value, string[] values) public string[] Values { get; } /// - public string Item => this.value; + public string Item { get; } /// public override string ToString() { - return value; + return this.Item; } /// public JToken ToJson() { - return new JValue(value); + return new JValue(this.Item); } /// public bool Equals(Enumerated other) { - return value == other.value; + return this.Item == other.Item; } /// public override bool Equals(object? obj) { if (obj is Enumerated enumerated) + { return this.Equals(enumerated); + } + return base.Equals(obj); } /// public override int GetHashCode() { - return value.GetHashCode(); + return this.Item.GetHashCode(); } /// public bool Equals([AllowNull] string other) { - return value == other; + return this.Item == other; } } } \ No newline at end of file diff --git a/src/GitIssue/Values/ITypeAlias.cs b/src/GitIssue/Values/ITypeAlias.cs index 372ad76..29dcb69 100644 --- a/src/GitIssue/Values/ITypeAlias.cs +++ b/src/GitIssue/Values/ITypeAlias.cs @@ -13,7 +13,7 @@ public interface ITypeAlias /// the alias to parse /// the type that matches the alias /// true if parsed, false otherwise - public bool TryParse(string alias, out Type type); + bool TryParse(string alias, out Type type); /// /// Tries to parse the type @@ -21,6 +21,6 @@ public interface ITypeAlias /// the type to parse /// the alias of the type /// true if parsed, false otherwise - public bool TryParse(Type type, out string alias); + bool TryParse(Type type, out string alias); } } \ No newline at end of file diff --git a/src/GitIssue/Values/IValue.cs b/src/GitIssue/Values/IValue.cs index c66d774..4805b4f 100644 --- a/src/GitIssue/Values/IValue.cs +++ b/src/GitIssue/Values/IValue.cs @@ -7,7 +7,6 @@ namespace GitIssue.Values /// public interface IValue { - } /// @@ -16,7 +15,7 @@ public interface IValue public interface IValue : IValue, IEquatable { /// - /// Gets the item backing the value + /// Gets the item backing the value /// T Item { get; } } diff --git a/src/GitIssue/Values/Label.cs b/src/GitIssue/Values/Label.cs index a4e67ea..c1b7d7f 100644 --- a/src/GitIssue/Values/Label.cs +++ b/src/GitIssue/Values/Label.cs @@ -13,8 +13,6 @@ namespace GitIssue.Values [TypeAlias(nameof(Label))] public struct Label : IJsonValue, IValue, IEquatable