diff --git a/.gitignore b/.gitignore index 792582c..7d56278 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /publish /*.zip /*.csv +/*.json /*.xlsx /output.txt /test.cmd diff --git a/CodeLineCounter.Tests/CoreUtilsTests.cs b/CodeLineCounter.Tests/CoreUtilsTests.cs index 3a72509..de1d79d 100644 --- a/CodeLineCounter.Tests/CoreUtilsTests.cs +++ b/CodeLineCounter.Tests/CoreUtilsTests.cs @@ -11,7 +11,7 @@ public void ParseArguments_Should_Return_Correct_Values() string[] args = ["-verbose", "-d", "testDirectory"]; // Act - var (Verbose, DirectoryPath, _) = CoreUtils.ParseArguments(args); + var (Verbose, DirectoryPath, _, _) = CoreUtils.ParseArguments(args); // Assert Assert.True(Verbose); @@ -25,7 +25,7 @@ public void ParseArguments_help_Should_Return_Correct_Values() string[] args = ["-help"]; // Act - var (_, _, Help) = CoreUtils.ParseArguments(args); + var (_, _, Help, _) = CoreUtils.ParseArguments(args); // Assert Assert.True(Help); @@ -38,7 +38,7 @@ public void ParseArguments_Should_Return_Default_Values_When_No_Arguments_Passed string[] args = []; // Act - var (Verbose, DirectoryPath, _) = CoreUtils.ParseArguments(args); + var (Verbose, DirectoryPath, _, _) = CoreUtils.ParseArguments(args); // Assert Assert.False(Verbose); @@ -49,10 +49,10 @@ public void ParseArguments_Should_Return_Default_Values_When_No_Arguments_Passed public void ParseArguments_Should_Ignore_Invalid_Arguments() { // Arrange - string[] args = ["-invalid", "-d", "testDirectory"]; + string[] args = ["-invalid", "-d", "testDirectory", "-f", "json"]; // Act - var (Verbose, DirectoryPath, _) = CoreUtils.ParseArguments(args); + var (Verbose, DirectoryPath, _, _) = CoreUtils.ParseArguments(args); // Assert Assert.False(Verbose); @@ -149,7 +149,7 @@ public void GetFilenamesList_Should_Return_List_Of_Filenames() public void CheckSettings_WhenHelpIsTrue_ReturnsFalse() { // Arrange - (bool Verbose, string? DirectoryPath, bool Help) settings = (true, null, true); + (bool Verbose, string? DirectoryPath, bool Help, CoreUtils.ExportFormat format) settings = (true, null, true, CoreUtils.ExportFormat.JSON); using var sw = new StringWriter(); Console.SetOut(sw); @@ -165,7 +165,7 @@ public void CheckSettings_WhenHelpIsTrue_ReturnsFalse() public void CheckSettings_WhenDirectoryPathIsNull_ReturnsFalse() { // Arrange - (bool Verbose, string? DirectoryPath, bool Help) settings = (Verbose: false, DirectoryPath: null, Help: false); + (bool Verbose, string? DirectoryPath, bool Help, CoreUtils.ExportFormat format) settings = (Verbose: false, DirectoryPath: null, Help: false, format: CoreUtils.ExportFormat.CSV); using var sw = new StringWriter(); Console.SetOut(sw); @@ -181,7 +181,7 @@ public void CheckSettings_WhenDirectoryPathIsNull_ReturnsFalse() public void CheckSettings_WhenSettingsAreValid_ReturnsTrue() { // Arrange - (bool Verbose, string DirectoryPath, bool Help) settings = (false, "some_directory", false); + (bool Verbose, string DirectoryPath, bool Help, CoreUtils.ExportFormat format) settings = (false, "some_directory", false, CoreUtils.ExportFormat.CSV); // Act var result = CoreUtils.CheckSettings(settings); @@ -189,5 +189,20 @@ public void CheckSettings_WhenSettingsAreValid_ReturnsTrue() // Assert Assert.True(result); } + + [Fact] + public void CheckSettings_WhenSettingsAreInvalid_ReturnsFalse() + { + // Arrange + (bool Verbose, string? DirectoryPath, bool Help, CoreUtils.ExportFormat format) settings = (false, null, false, CoreUtils.ExportFormat.CSV); + using var sw = new StringWriter(); + Console.SetOut(sw); + + // Act + var result = CoreUtils.CheckSettings(settings); + + // Assert + Assert.False(result); + } } } \ No newline at end of file diff --git a/CodeLineCounter.Tests/CsvExporterTests.cs b/CodeLineCounter.Tests/CsvExporterTests.cs deleted file mode 100644 index aa7590d..0000000 --- a/CodeLineCounter.Tests/CsvExporterTests.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Xunit; -using System.IO; -using System.Collections.Generic; -using CodeLineCounter.Models; -using CodeLineCounter.Utils; - -namespace CodeLineCounter.Tests -{ - public class CsvExporterTests - { - [Fact] - public void ExportToCsv_ValidData_WritesToFile() - { - // Arrange - string filePath = "test1.csv"; - var namespaceMetrics = GetSampleNamespaceMetrics(); - var projectTotals = GetSampleProjectTotals(); - int totalLines = 500; - var duplicationCodes = GetSampleDuplicationCodes(); - string? additionalInfo = "Some additional info"; - - // Act - CsvExporter.ExportToCsv(filePath, namespaceMetrics, projectTotals, totalLines, duplicationCodes, additionalInfo); - - // Assert - Assert.True(File.Exists(filePath)); - var lines = File.ReadAllLines(filePath); - Assert.Equal(6, lines.Length); // Header + 2 records + 2 sub-totals + 1 total - - // Cleanup - File.Delete(filePath); - } - - [Fact] - public void ExportToCsv_EmptyData_WritesEmptyFile() - { - // Arrange - string filePath = "test2.csv"; - var namespaceMetrics = new List(); - var projectTotals = new Dictionary(); - int totalLines = 0; - var duplicationCodes = new List(); - string? additionalInfo = null; - - // Act - CsvExporter.ExportToCsv(filePath, namespaceMetrics, projectTotals, totalLines, duplicationCodes, additionalInfo); - - // Assert - Assert.True(File.Exists(filePath)); - var lines = File.ReadAllLines(filePath); - Assert.Single(lines); // Only header - - // Cleanup - File.Delete(filePath); - } - - [Fact] - public void ExportToCsv_NullData_ThrowsNullReferenceException() - { - // Arrange - string filePath = "test3.csv"; - int totalLines = 0; - - // Act & Assert - #pragma warning disable CS8625 - Assert.Throws(() => CsvExporter.ExportToCsv(filePath, null, null, totalLines, null, null)); - } - - [Fact] - public void ExportCodeDuplicationsToCsv_ShouldCallSerializeWithCorrectParameters() - { - // Arrange - var filePath = "test4.csv"; - var duplications = new List - { - new() { CodeHash = "hash1", FilePath = "file1.cs", MethodName = "method1", StartLine = 10, NbLines = 20 }, - new() { CodeHash = "hash2", FilePath = "file2.cs", MethodName = "method2", StartLine = 8, NbLines = 10 } - }; - - - // Act - CsvExporter.ExportCodeDuplicationsToCsv(filePath, duplications); - - // Assert - Assert.True(File.Exists(filePath)); - var lines = File.ReadAllLines(filePath); - Assert.Equal(3, lines.Length); - - // Cleanup - File.Delete(filePath); - - } - - private static List GetSampleNamespaceMetrics() - { - return - [ - new NamespaceMetrics { ProjectName="Project1", ProjectPath = ".", NamespaceName = "Namespace1", FileName = "File1", FilePath = ".", LineCount = 100, CyclomaticComplexity = 0, CodeDuplications = 0 }, - new NamespaceMetrics { ProjectName="Project2", ProjectPath = ".", NamespaceName = "Namespace2", FileName = "File2", FilePath = ".", LineCount = 200, CyclomaticComplexity = 5, CodeDuplications = 2 } - ]; - } - - private static Dictionary GetSampleProjectTotals() - { - return new Dictionary - { - {"Project1", 100}, - {"Project2", 200} - }; - } - - private static List GetSampleDuplicationCodes() - { - return - [ - new DuplicationCode { CodeHash = "Code1", FilePath = ".", MethodName = "Method1", StartLine = 10 , NbLines = 20 }, - new DuplicationCode { CodeHash = "Code2", FilePath = ".", MethodName = "Method2", StartLine = 15 , NbLines = 25 } - ]; - } - } -} \ No newline at end of file diff --git a/CodeLineCounter.Tests/DataExporterTests.cs b/CodeLineCounter.Tests/DataExporterTests.cs new file mode 100644 index 0000000..aa4ed06 --- /dev/null +++ b/CodeLineCounter.Tests/DataExporterTests.cs @@ -0,0 +1,135 @@ +using Xunit; +using System; +using System.IO; +using System.Collections.Generic; +using CodeLineCounter.Models; +using CodeLineCounter.Utils; +using System.Linq; + +namespace CodeLineCounter.Tests +{ + public class DataExporterTests : IDisposable + { + private readonly string _testDirectory; + private bool _disposed; + + public DataExporterTests() + { + _testDirectory = Path.Combine(Path.GetTempPath(), "DataExporterTests"); + Directory.CreateDirectory(_testDirectory); + } + + [Theory] + [InlineData(CoreUtils.ExportFormat.CSV, ".csv")] + [InlineData(CoreUtils.ExportFormat.JSON, ".json")] + public void Export_SingleItem_CreatesFileWithCorrectExtension(CoreUtils.ExportFormat format, string expectedExtension) + { + // Arrange + var testItem = new TestClass { Id = 1, Name = "Test" }; + var filePath = Path.Combine(_testDirectory, "test"); + + // Act + DataExporter.Export(filePath, testItem, format); + + // Assert + Assert.True(File.Exists(filePath + expectedExtension)); + } + + [Theory] + [InlineData(CoreUtils.ExportFormat.CSV, ".csv")] + [InlineData(CoreUtils.ExportFormat.JSON, ".json")] + public void ExportCollection_WithMultipleItems_CreatesFile(CoreUtils.ExportFormat format, string expectedExtension) + { + // Arrange + var items = new List + { + new() { Id = 1, Name = "Test1" }, + new() { Id = 2, Name = "Test2" } + }; + var filePath = Path.Combine(_testDirectory, "collection"); + + // Act + DataExporter.ExportCollection(filePath, items, format); + + // Assert + Assert.True(File.Exists(filePath + expectedExtension)); + } + + [Fact] + public void ExportMetrics_CreatesFileWithCorrectData() + { + // Arrange + var metrics = new List + { + new() { ProjectName = "Project1", LineCount = 100 }, + new() { ProjectName = "Project2", LineCount = 200 } + }; + var projectTotals = new Dictionary + { + { "Project1", 100 }, + { "Project2", 200 } + }; + var duplications = new List(); + var filePath = Path.Combine(_testDirectory, "metrics"); + + // Act + DataExporter.ExportMetrics(filePath, metrics, projectTotals, 300, duplications, null, CoreUtils.ExportFormat.CSV); + + // Assert + Assert.True(File.Exists(filePath + ".csv")); + } + + [Fact] + public void ExportDuplications_CreatesFileWithCorrectData() + { + // Arrange + var duplications = new List + { + new() { CodeHash = "hash1", FilePath = "file1.cs", MethodName = "method1", StartLine = 10, NbLines = 20 }, + new() { CodeHash = "hash2", FilePath = "file2.cs", MethodName = "method2", StartLine = 8, NbLines = 10 } + }; + var filePath = Path.Combine(_testDirectory, "duplications"); + + // Act + DataExporter.ExportDuplications(filePath, duplications, CoreUtils.ExportFormat.CSV); + + // Assert + Assert.True(File.Exists(filePath + ".csv")); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing && Directory.Exists(_testDirectory)) + { + // Dispose managed resources + Directory.Delete(_testDirectory, true); + } + + // Dispose unmanaged resources (if any) + + _disposed = true; + } + } + + private class TestClass + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~DataExporterTests() + { + Dispose(false); + } + + + } +} \ No newline at end of file diff --git a/CodeLineCounter/Program.cs b/CodeLineCounter/Program.cs index c02925b..8788888 100644 --- a/CodeLineCounter/Program.cs +++ b/CodeLineCounter/Program.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Collections.Generic; +using System.Collections.Specialized; namespace CodeLineCounter { @@ -30,17 +31,19 @@ static void Main(string[] args) if (choice == -1) return; var solutionPath = Path.GetFullPath(solutionFiles[choice - 1]); - AnalyzeAndExportSolution(solutionPath, settings.Verbose); + AnalyzeAndExportSolution(solutionPath, settings.Verbose, settings.format); } } - private static void AnalyzeAndExportSolution(string solutionPath, bool verbose) + private static void AnalyzeAndExportSolution(string solutionPath, bool verbose, CoreUtils.ExportFormat format ) { var timer = new Stopwatch(); timer.Start(); string solutionFilename = Path.GetFileName(solutionPath); string csvFilePath = $"{solutionFilename}-CodeMetrics.csv"; + csvFilePath = CoreUtils.GetExportFileNameWithExtension(csvFilePath, format); string duplicationCsvFilePath = $"{solutionFilename}-CodeDuplications.csv"; + duplicationCsvFilePath = CoreUtils.GetExportFileNameWithExtension(duplicationCsvFilePath, format); var (metrics, projectTotals, totalLines, totalFiles, duplicationMap) = CodeAnalyzer.AnalyzeSolution(solutionPath); timer.Stop(); @@ -67,8 +70,10 @@ private static void AnalyzeAndExportSolution(string solutionPath, bool verbose) Console.WriteLine($"Percentage of duplicated code: {percentageDuplication:F2} %"); Parallel.Invoke( - () => CsvExporter.ExportToCsv(csvFilePath, metrics, projectTotals, totalLines, duplicationMap, solutionPath), - () => CsvExporter.ExportCodeDuplicationsToCsv(duplicationCsvFilePath, duplicationMap) + //() => CsvExporter.ExportToCsv(csvFilePath, metrics, projectTotals, totalLines, duplicationMap, solutionPath), + //() => CsvExporter.ExportCodeDuplicationsToCsv(duplicationCsvFilePath, duplicationMap), + () => DataExporter.ExportMetrics(csvFilePath, metrics, projectTotals, totalLines, duplicationMap, solutionPath, format), + () => DataExporter.ExportDuplications(duplicationCsvFilePath, duplicationMap, format) ); Console.WriteLine($"The data has been exported to {csvFilePath}"); diff --git a/CodeLineCounter/Utils/CoreUtils.cs b/CodeLineCounter/Utils/CoreUtils.cs index 08225ac..8de86b1 100644 --- a/CodeLineCounter/Utils/CoreUtils.cs +++ b/CodeLineCounter/Utils/CoreUtils.cs @@ -4,10 +4,16 @@ namespace CodeLineCounter.Utils { public static class CoreUtils { - public static (bool Verbose, string? DirectoryPath, bool Help) ParseArguments(string[] args) + public enum ExportFormat + { + CSV, + JSON + } + public static (bool Verbose, string? DirectoryPath, bool Help, ExportFormat format) ParseArguments(string[] args) { bool verbose = false; bool help = false; + ExportFormat format = ExportFormat.CSV; string? directoryPath = null; int argIndex = 0; while (argIndex < args.Length) @@ -33,12 +39,27 @@ public static (bool Verbose, string? DirectoryPath, bool Help) ParseArguments(st argIndex++; // Increment by 1 if there's no next argument } break; + case "-format": + if (argIndex + 1 < args.Length) + { + string formatString = args[argIndex + 1]; + argIndex += 2; // Increment by 2 to skip the next argument + if (Enum.TryParse(formatString, true, out ExportFormat result)) + { + format = result; + } + else + { + Console.WriteLine($"Invalid format: {formatString}. Valid formats are: {string.Join(", ", Enum.GetNames(typeof(ExportFormat)))}. Using default format {format}"); + } + } + break; default: argIndex++; break; } } - return (verbose, directoryPath, help); + return (verbose, directoryPath, help, format); } public static int GetUserChoice(int solutionCount) @@ -83,11 +104,11 @@ public static void DisplaySolutions(List solutionFiles) } } - public static bool CheckSettings((bool Verbose, string? DirectoryPath, bool Help) settings) + public static bool CheckSettings((bool Verbose, string? DirectoryPath, bool Help, ExportFormat format) settings) { if (settings.Help) { - Console.WriteLine("Usage: CodeLineCounter.exe [-verbose] [-d ] [-help, -h]"); + Console.WriteLine("Usage: CodeLineCounter.exe [-verbose] [-d ] [-help, -h] (-format )"); return false; } @@ -99,6 +120,21 @@ public static bool CheckSettings((bool Verbose, string? DirectoryPath, bool Help return true; } + + public static string GetExportFileNameWithExtension(string filePath, CoreUtils.ExportFormat format) + { + switch (format) + { + case CoreUtils.ExportFormat.CSV: + filePath = Path.ChangeExtension(filePath, ".csv"); + break; + case CoreUtils.ExportFormat.JSON: + filePath = Path.ChangeExtension(filePath, ".json"); + break; + } + + return filePath; + } } } \ No newline at end of file diff --git a/CodeLineCounter/Utils/CsvExporter.cs b/CodeLineCounter/Utils/CsvExporter.cs deleted file mode 100644 index 9286d88..0000000 --- a/CodeLineCounter/Utils/CsvExporter.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.IO; -using System.Collections.Generic; -using CodeLineCounter.Utils; -using CodeLineCounter.Models; - -namespace CodeLineCounter.Utils -{ - public static class CsvExporter - { - public static void ExportToCsv(string filePath, List metrics, Dictionary projectTotals, int totalLines, List duplications, string? solutionPath) - { - string? currentProject = null; - List namespaceMetrics = []; - var duplicationCounts = GetDuplicationCounts(duplications); - - foreach (var metric in metrics) - { - if (!string.Equals(currentProject, metric.ProjectName, System.StringComparison.OrdinalIgnoreCase)) - { - if (currentProject != null) - { - var total = new NamespaceMetrics - { - ProjectName = currentProject, - ProjectPath = "Total", - LineCount = projectTotals[currentProject] - }; - namespaceMetrics.Add(total); - } - currentProject = metric.ProjectName; - } - int fileDuplicationCount = GetFileDuplicationsCount(duplicationCounts, metric, solutionPath); - metric.CodeDuplications = fileDuplicationCount; - namespaceMetrics.Add(metric); - - } - if (currentProject != null) - { - var total = new NamespaceMetrics - { - ProjectName = currentProject, - ProjectPath = "Total", - LineCount = projectTotals[currentProject] - }; - namespaceMetrics.Add(total); - var totalGeneral = new NamespaceMetrics - { - ProjectName = "Total", - ProjectPath = "", - LineCount = totalLines - }; - namespaceMetrics.Add(totalGeneral); - - } - CsvHandler.Serialize(namespaceMetrics, filePath); - } - - - public static void ExportCodeDuplicationsToCsv(string filePath, List duplications) - { - CsvHandler.Serialize(duplications, filePath); - } - - public static Dictionary GetDuplicationCounts(List duplications) - { - var duplicationCounts = new Dictionary(); - - foreach (var duplication in duplications) - { - var normalizedPath = Path.GetFullPath(duplication.FilePath); - if (duplicationCounts.TryGetValue(normalizedPath, out int count)) - { - duplicationCounts[normalizedPath] = count + 1; - } - else - { - duplicationCounts[normalizedPath] = 1; - } - } - - return duplicationCounts; - } - - public static int GetFileDuplicationsCount(Dictionary duplicationCounts, NamespaceMetrics metric, string? solutionPath) - { - int count = 0; - if (solutionPath == null) - { - solutionPath = Path.GetFullPath("."); - } - else - { - solutionPath = Path.GetDirectoryName(solutionPath); - } - if (metric.FilePath != null && solutionPath != null) - { - var normalizedPath = !string.Equals(solutionPath, string.Empty, System.StringComparison.OrdinalIgnoreCase) ? Path.GetFullPath(metric.FilePath, solutionPath) : Path.GetFullPath(metric.FilePath); - if (duplicationCounts.TryGetValue(normalizedPath, out int countValue)) - { - count = countValue; - } - else - { - count = 0; - } - } - - return count; - } - } -} - diff --git a/CodeLineCounter/Utils/DataExporter.cs b/CodeLineCounter/Utils/DataExporter.cs new file mode 100644 index 0000000..a0aeae7 --- /dev/null +++ b/CodeLineCounter/Utils/DataExporter.cs @@ -0,0 +1,127 @@ +using System.IO; +using System.Collections.Generic; +using CodeLineCounter.Utils; +using CodeLineCounter.Models; + +namespace CodeLineCounter.Utils +{ + + public static class DataExporter + { + public static void Export(string filePath, T data, CoreUtils.ExportFormat format) where T : class + { + filePath = CoreUtils.GetExportFileNameWithExtension(filePath, format); + switch (format) + { + case CoreUtils.ExportFormat.CSV: + CsvHandler.Serialize(new List { data }, filePath); + break; + case CoreUtils.ExportFormat.JSON: + JsonHandler.Serialize(new List { data }, filePath); + break; + } + } + + public static void ExportCollection(string filePath, IEnumerable data, CoreUtils.ExportFormat format) where T : class + { + filePath = CoreUtils.GetExportFileNameWithExtension(filePath, format); + switch (format) + { + case CoreUtils.ExportFormat.CSV: + CsvHandler.Serialize(data, filePath); + break; + case CoreUtils.ExportFormat.JSON: + JsonHandler.Serialize(data, filePath); + break; + } + } + + public static void ExportDuplications(string filePath, List duplications, CoreUtils.ExportFormat format) + { + ExportCollection(filePath, duplications, format); + } + + public static void ExportMetrics(string filePath, List metrics, + Dictionary projectTotals, int totalLines, + List duplications, string? solutionPath, CoreUtils.ExportFormat format) + { + string? currentProject = null; + filePath = CoreUtils.GetExportFileNameWithExtension(filePath, format); + List namespaceMetrics = []; + var duplicationCounts = GetDuplicationCounts(duplications); + + foreach (var metric in metrics) + { + if (!string.Equals(currentProject, metric.ProjectName, System.StringComparison.OrdinalIgnoreCase)) + { + if (currentProject != null) + { + namespaceMetrics.Add(new NamespaceMetrics + { + ProjectName = currentProject, + ProjectPath = "Total", + LineCount = projectTotals[currentProject] + }); + } + currentProject = metric.ProjectName; + } + metric.CodeDuplications = GetFileDuplicationsCount(duplicationCounts, metric, solutionPath); + namespaceMetrics.Add(metric); + } + + if (currentProject != null) + { + namespaceMetrics.Add(new NamespaceMetrics + { + ProjectName = currentProject, + ProjectPath = "Total", + LineCount = projectTotals[currentProject] + }); + namespaceMetrics.Add(new NamespaceMetrics + { + ProjectName = "Total", + ProjectPath = "", + LineCount = totalLines + }); + } + + ExportCollection(filePath, namespaceMetrics, format); + } + + private static Dictionary GetDuplicationCounts(List duplications) + { + var duplicationCounts = new Dictionary(); + foreach (var duplication in duplications) + { + var normalizedPath = Path.GetFullPath(duplication.FilePath); + if (duplicationCounts.TryGetValue(normalizedPath, out int count)) + { + duplicationCounts[normalizedPath] = count + 1; + } + else + { + duplicationCounts[normalizedPath] = 1; + } + } + return duplicationCounts; + } + + private static int GetFileDuplicationsCount(Dictionary duplicationCounts, + NamespaceMetrics metric, string? solutionPath) + { + if (metric.FilePath == null) return 0; + + solutionPath = solutionPath == null ? + Path.GetFullPath(".") : + Path.GetDirectoryName(solutionPath); + + if (solutionPath == null) return 0; + + var normalizedPath = !string.IsNullOrEmpty(solutionPath) ? + Path.GetFullPath(metric.FilePath, solutionPath) : + Path.GetFullPath(metric.FilePath); + + return duplicationCounts.TryGetValue(normalizedPath, out int count) ? count : 0; + } + } +} \ No newline at end of file diff --git a/CodeLineCounter/Utils/JSONHandler.cs b/CodeLineCounter/Utils/JSONHandler.cs new file mode 100644 index 0000000..05f193d --- /dev/null +++ b/CodeLineCounter/Utils/JSONHandler.cs @@ -0,0 +1,26 @@ +using System.Text.Json; + +namespace CodeLineCounter.Utils +{ + public static class JsonHandler + { + private static readonly JsonSerializerOptions _options = new() + { + WriteIndented = true, + PropertyNameCaseInsensitive = true + }; + + public static void Serialize(IEnumerable data, string filePath) + { + string jsonString = JsonSerializer.Serialize(data, _options); + File.WriteAllText(filePath, jsonString); + } + + public static IEnumerable Deserialize(string filePath) + { + string jsonString = File.ReadAllText(filePath); + return JsonSerializer.Deserialize>(jsonString, _options) + ?? []; + } + } +} \ No newline at end of file