From 085e40d5ecaaff325bb1f3a86b8937c3e8ef28ee Mon Sep 17 00:00:00 2001 From: Sov3rain Date: Sun, 6 Oct 2024 14:52:13 +0200 Subject: [PATCH] clean up and restructuring of the codebase. --- Assets/Scripts/CSVExample.cs | 6 +- Assets/uni-csv/Runtime/CSVParser.cs | 76 +------------------ Assets/uni-csv/Runtime/CsvColumnAttribute.cs | 15 ++++ .../Runtime/CsvColumnAttribute.cs.meta | 3 + Assets/uni-csv/Runtime/Delimiter.cs | 63 +++++++++++++++ Assets/uni-csv/Runtime/Delimiter.cs.meta | 3 + Assets/uni-csv/Runtime/Sov3rain.UniCSV.asmdef | 3 + ...smdef.meta => Sov3rain.UniCSV.asmdef.meta} | 0 .../uni-csv/Runtime/Sov3rain.Unity-CSV.asmdef | 3 - .../Tests/Editor/AttributeMappingTests.cs | 8 +- .../uni-csv/Tests/Editor/BasicParsingTests.cs | 6 +- Assets/uni-csv/Tests/Editor/DelimiterTests.cs | 6 +- .../uni-csv/Tests/Editor/FileHandlingTests.cs | 4 +- ...ef => Sov3rain.UniCSV.Editor.Tests.asmdef} | 2 +- ... Sov3rain.UniCSV.Editor.Tests.asmdef.meta} | 0 Assets/uni-csv/package.json | 2 +- CHANGELOG.md | 12 +++ README.md | 14 ++-- 18 files changed, 127 insertions(+), 99 deletions(-) create mode 100644 Assets/uni-csv/Runtime/CsvColumnAttribute.cs create mode 100644 Assets/uni-csv/Runtime/CsvColumnAttribute.cs.meta create mode 100644 Assets/uni-csv/Runtime/Delimiter.cs create mode 100644 Assets/uni-csv/Runtime/Delimiter.cs.meta create mode 100644 Assets/uni-csv/Runtime/Sov3rain.UniCSV.asmdef rename Assets/uni-csv/Runtime/{Sov3rain.Unity-CSV.asmdef.meta => Sov3rain.UniCSV.asmdef.meta} (100%) delete mode 100644 Assets/uni-csv/Runtime/Sov3rain.Unity-CSV.asmdef rename Assets/uni-csv/Tests/Editor/{Sov3rain.Unity-CSV.Editor.Tests.asmdef => Sov3rain.UniCSV.Editor.Tests.asmdef} (92%) rename Assets/uni-csv/Tests/Editor/{Sov3rain.Unity-CSV.Editor.Tests.asmdef.meta => Sov3rain.UniCSV.Editor.Tests.asmdef.meta} (100%) diff --git a/Assets/Scripts/CSVExample.cs b/Assets/Scripts/CSVExample.cs index 329cbfd..0f7a27b 100644 --- a/Assets/Scripts/CSVExample.cs +++ b/Assets/Scripts/CSVExample.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Sov3rain; +using UniCSV; using UnityEngine; public class CSVExample : MonoBehaviour @@ -27,14 +27,14 @@ private void Start() LoadCSV(_usernamesCSV); LoadCSV(_csvWithEmptyLines); - var users = CSVParser.ParseFromString(_usernamesCSV.text).ToList(); + var users = CsvParser.ParseFromString(_usernamesCSV.text).ToList(); } private void LoadCSV(TextAsset usernamesCsv) { try { - var data = CSVParser.ParseFromString(usernamesCsv.text); + var data = CsvParser.ParseFromString(usernamesCsv.text); Debug.Log($"Loaded {data.Count} rows from file"); diff --git a/Assets/uni-csv/Runtime/CSVParser.cs b/Assets/uni-csv/Runtime/CSVParser.cs index ab979c6..0028674 100644 --- a/Assets/uni-csv/Runtime/CSVParser.cs +++ b/Assets/uni-csv/Runtime/CSVParser.cs @@ -5,23 +5,12 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; -using static Sov3rain.CSVParser.Delimiter; +using static UniCSV.Delimiter; -namespace Sov3rain +namespace UniCSV { - public static class CSVParser + public static class CsvParser { - private static readonly char[] COMMON_DELIMITERS = { ',', '\t', ';', '|' }; - - public enum Delimiter - { - Auto, - Comma, - Tab, - Semicolon, - Pipe - } - /// /// Load CSV data from a specified path. /// @@ -112,7 +101,7 @@ public static List> ParseFromString( { if (delimiter == Auto) { - delimiter = DetectDelimiterFromContent(data); + delimiter = DelimiterUtils.DetectDelimiterFromContent(data); } ConvertToCrlf(ref data); @@ -205,31 +194,6 @@ public static List> ParseFromString( return sheet; } - private static Delimiter DetectDelimiterFromContent(string content) - { - if (string.IsNullOrWhiteSpace(content)) - return Comma; - - // Get the first non-empty line - var firstLine = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None) - .FirstOrDefault(line => !string.IsNullOrWhiteSpace(line)); - - if (string.IsNullOrWhiteSpace(firstLine)) - return Comma; - - var delimiterCounts = COMMON_DELIMITERS - .ToDictionary(d => d, d => firstLine.Count(c => c == d)); - - var mostFrequentDelimiter = delimiterCounts - .OrderByDescending(kvp => kvp.Value) - .First(); - - if (mostFrequentDelimiter.Value <= 1) - return Comma; - - return CharToDelimiter(mostFrequentDelimiter.Key); - } - private static bool IsRowNonEmpty(List row) => row.Count > 0 && row.Any(cell => !string.IsNullOrWhiteSpace(cell)); @@ -251,24 +215,6 @@ private static void ConvertToCrlf(ref string data) data = Regex.Replace(data, @"\r\n|\r|\n", "\r\n"); } - private static char ToChar(this Delimiter delimiter) => delimiter switch - { - Comma => ',', - Tab => '\t', - Semicolon => ';', - Pipe => '|', - _ => throw new ArgumentException($"Unsupported delimiter: {delimiter}") - }; - - private static Delimiter CharToDelimiter(char delimiterChar) => delimiterChar switch - { - ',' => Comma, - '\t' => Tab, - ';' => Semicolon, - '|' => Pipe, - _ => Comma - }; - private static object ConvertValue(string value, Type targetType) { if (string.IsNullOrWhiteSpace(value)) @@ -279,9 +225,6 @@ private static object ConvertValue(string value, Type targetType) return targetType.IsValueType ? Activator.CreateInstance(targetType) : null; } - if (string.IsNullOrWhiteSpace(value)) - return targetType.IsValueType ? Activator.CreateInstance(targetType) : null; - if (targetType == typeof(string)) return value; @@ -303,15 +246,4 @@ private static object ConvertValue(string value, Type targetType) throw new NotSupportedException($"Type {targetType} is not supported for conversion."); } } -} - -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] -public class CsvColumnAttribute : Attribute -{ - public string Name { get; } - - public CsvColumnAttribute(string name) - { - Name = name; - } } \ No newline at end of file diff --git a/Assets/uni-csv/Runtime/CsvColumnAttribute.cs b/Assets/uni-csv/Runtime/CsvColumnAttribute.cs new file mode 100644 index 0000000..13285f2 --- /dev/null +++ b/Assets/uni-csv/Runtime/CsvColumnAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace UniCSV +{ + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] + public sealed class CsvColumnAttribute : Attribute + { + public string Name { get; } + + public CsvColumnAttribute(string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/Assets/uni-csv/Runtime/CsvColumnAttribute.cs.meta b/Assets/uni-csv/Runtime/CsvColumnAttribute.cs.meta new file mode 100644 index 0000000..c9eab08 --- /dev/null +++ b/Assets/uni-csv/Runtime/CsvColumnAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e9c68525124d40f5bfbfd7397240e9a7 +timeCreated: 1728217898 \ No newline at end of file diff --git a/Assets/uni-csv/Runtime/Delimiter.cs b/Assets/uni-csv/Runtime/Delimiter.cs new file mode 100644 index 0000000..193a425 --- /dev/null +++ b/Assets/uni-csv/Runtime/Delimiter.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using static UniCSV.Delimiter; + +namespace UniCSV +{ + public enum Delimiter + { + Auto, + Comma, + Tab, + Semicolon, + Pipe + } + + public static class DelimiterUtils + { + private static readonly char[] COMMON_DELIMITERS = { ',', '\t', ';', '|' }; + + public static Delimiter DetectDelimiterFromContent(string content) + { + if (string.IsNullOrWhiteSpace(content)) + return Comma; + + // Get the first non-empty line + var firstLine = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None) + .FirstOrDefault(line => !string.IsNullOrWhiteSpace(line)); + + if (string.IsNullOrWhiteSpace(firstLine)) + return Comma; + + var delimiterCounts = COMMON_DELIMITERS + .ToDictionary(d => d, d => firstLine.Count(c => c == d)); + + var mostFrequentDelimiter = delimiterCounts + .OrderByDescending(kvp => kvp.Value) + .First(); + + if (mostFrequentDelimiter.Value <= 1) + return Comma; + + return CharToDelimiter(mostFrequentDelimiter.Key); + } + + public static char ToChar(this Delimiter delimiter) => delimiter switch + { + Comma => ',', + Tab => '\t', + Semicolon => ';', + Pipe => '|', + _ => throw new ArgumentException($"Unsupported delimiter: {delimiter}") + }; + + public static Delimiter CharToDelimiter(char delimiterChar) => delimiterChar switch + { + ',' => Comma, + '\t' => Tab, + ';' => Semicolon, + '|' => Pipe, + _ => Comma + }; + } +} \ No newline at end of file diff --git a/Assets/uni-csv/Runtime/Delimiter.cs.meta b/Assets/uni-csv/Runtime/Delimiter.cs.meta new file mode 100644 index 0000000..35f593e --- /dev/null +++ b/Assets/uni-csv/Runtime/Delimiter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bb4c3ea853f64de0b56cfb4dfcb67e0a +timeCreated: 1728218184 \ No newline at end of file diff --git a/Assets/uni-csv/Runtime/Sov3rain.UniCSV.asmdef b/Assets/uni-csv/Runtime/Sov3rain.UniCSV.asmdef new file mode 100644 index 0000000..1c004cf --- /dev/null +++ b/Assets/uni-csv/Runtime/Sov3rain.UniCSV.asmdef @@ -0,0 +1,3 @@ +{ + "name": "Sov3rain.UniCSV" +} diff --git a/Assets/uni-csv/Runtime/Sov3rain.Unity-CSV.asmdef.meta b/Assets/uni-csv/Runtime/Sov3rain.UniCSV.asmdef.meta similarity index 100% rename from Assets/uni-csv/Runtime/Sov3rain.Unity-CSV.asmdef.meta rename to Assets/uni-csv/Runtime/Sov3rain.UniCSV.asmdef.meta diff --git a/Assets/uni-csv/Runtime/Sov3rain.Unity-CSV.asmdef b/Assets/uni-csv/Runtime/Sov3rain.Unity-CSV.asmdef deleted file mode 100644 index 568ba20..0000000 --- a/Assets/uni-csv/Runtime/Sov3rain.Unity-CSV.asmdef +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "Uni-CSV" -} diff --git a/Assets/uni-csv/Tests/Editor/AttributeMappingTests.cs b/Assets/uni-csv/Tests/Editor/AttributeMappingTests.cs index 672afee..70e827f 100644 --- a/Assets/uni-csv/Tests/Editor/AttributeMappingTests.cs +++ b/Assets/uni-csv/Tests/Editor/AttributeMappingTests.cs @@ -1,7 +1,7 @@ using System.Linq; using NUnit.Framework; -namespace Sov3rain.Tests +namespace UniCSV.Tests { [TestFixture] public class AttributeMappingTests @@ -34,7 +34,7 @@ private class NullableTypesClass public void ParseFromString_WithAttributes_MapsCorrectly() { string csvData = "FirstName,YearsOld,Country\nJohn,25,USA"; - var result = CSVParser.ParseFromString(csvData).ToList(); + var result = CsvParser.ParseFromString(csvData).ToList(); Assert.AreEqual(1, result.Count); Assert.AreEqual("John", result[0].Name); @@ -46,7 +46,7 @@ public void ParseFromString_WithAttributes_MapsCorrectly() public void ParseFromString_WithAttributes_HandlesEmptyValues() { string csvData = "FirstName,YearsOld,Country\n,25,\nJane,,UK"; - var result = CSVParser.ParseFromString(csvData).ToList(); + var result = CsvParser.ParseFromString(csvData).ToList(); Assert.AreEqual(2, result.Count); @@ -63,7 +63,7 @@ public void ParseFromString_WithAttributes_HandlesEmptyValues() public void ParseFromString_HandlesNullableTypes() { string csvData = "Int,Decimal,String\n,,\nInvalid,Invalid,Value"; - var result = CSVParser.ParseFromString(csvData).ToList(); + var result = CsvParser.ParseFromString(csvData).ToList(); Assert.AreEqual(1, result.Count); Assert.IsNull(result[0].NullableInt); diff --git a/Assets/uni-csv/Tests/Editor/BasicParsingTests.cs b/Assets/uni-csv/Tests/Editor/BasicParsingTests.cs index 5211c30..c908d7e 100644 --- a/Assets/uni-csv/Tests/Editor/BasicParsingTests.cs +++ b/Assets/uni-csv/Tests/Editor/BasicParsingTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Sov3rain.Tests +namespace UniCSV.Tests { [TestFixture] public class BasicParsingTests @@ -9,7 +9,7 @@ public class BasicParsingTests public void ParseFromString_CommaDelimited_ReturnsCorrectData() { string csvData = "Name,Age,Location\nJohn,25,USA\nJane,30,UK"; - var result = CSVParser.ParseFromString(csvData, delimiter: CSVParser.Delimiter.Comma); + var result = CsvParser.ParseFromString(csvData, delimiter: Delimiter.Comma); Assert.AreEqual(2, result.Count); Assert.AreEqual("John", result[0][0]); @@ -21,7 +21,7 @@ public void ParseFromString_CommaDelimited_ReturnsCorrectData() public void ParseFromString_EmptyFile_ReturnsEmptyList() { string csvData = ""; - var result = CSVParser.ParseFromString(csvData); + var result = CsvParser.ParseFromString(csvData); Assert.AreEqual(0, result.Count); } diff --git a/Assets/uni-csv/Tests/Editor/DelimiterTests.cs b/Assets/uni-csv/Tests/Editor/DelimiterTests.cs index 6633bd0..32c70ef 100644 --- a/Assets/uni-csv/Tests/Editor/DelimiterTests.cs +++ b/Assets/uni-csv/Tests/Editor/DelimiterTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Sov3rain.Tests +namespace UniCSV.Tests { [TestFixture] public class DelimiterTests @@ -9,7 +9,7 @@ public class DelimiterTests public void ParseFromString_AutoDetectDelimiter_SemicolonDelimited() { string csvData = "Name;Age;Location\nJohn;25;USA"; - var result = CSVParser.ParseFromString(csvData); + var result = CsvParser.ParseFromString(csvData); Assert.AreEqual(1, result.Count); Assert.AreEqual("John", result[0][0]); @@ -20,7 +20,7 @@ public void ParseFromString_AutoDetectDelimiter_SemicolonDelimited() [TestCase("Name|Age|Location\nJohn|25|USA", TestName = "PipeDelimited")] public void ParseFromString_AutoDetectDelimiter(string csvData) { - var result = CSVParser.ParseFromString(csvData); + var result = CsvParser.ParseFromString(csvData); Assert.AreEqual(1, result.Count); Assert.AreEqual("John", result[0][0]); diff --git a/Assets/uni-csv/Tests/Editor/FileHandlingTests.cs b/Assets/uni-csv/Tests/Editor/FileHandlingTests.cs index b8545d3..54fa058 100644 --- a/Assets/uni-csv/Tests/Editor/FileHandlingTests.cs +++ b/Assets/uni-csv/Tests/Editor/FileHandlingTests.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace Sov3rain.Tests +namespace UniCSV.Tests { [TestFixture] public class FileHandlingTests @@ -9,7 +9,7 @@ public class FileHandlingTests public void ParseFromPath_WithInvalidPath_ThrowsFileNotFoundException() { Assert.Throws(() => - CSVParser.ParseFromPath("nonexistent.csv")); + CsvParser.ParseFromPath("nonexistent.csv")); } } } \ No newline at end of file diff --git a/Assets/uni-csv/Tests/Editor/Sov3rain.Unity-CSV.Editor.Tests.asmdef b/Assets/uni-csv/Tests/Editor/Sov3rain.UniCSV.Editor.Tests.asmdef similarity index 92% rename from Assets/uni-csv/Tests/Editor/Sov3rain.Unity-CSV.Editor.Tests.asmdef rename to Assets/uni-csv/Tests/Editor/Sov3rain.UniCSV.Editor.Tests.asmdef index 3483858..2b465df 100644 --- a/Assets/uni-csv/Tests/Editor/Sov3rain.Unity-CSV.Editor.Tests.asmdef +++ b/Assets/uni-csv/Tests/Editor/Sov3rain.UniCSV.Editor.Tests.asmdef @@ -1,5 +1,5 @@ { - "name": "Sov3rain.Unity-CSV.Editor.Tests", + "name": "Sov3rain.UniCSV.Editor.Tests", "rootNamespace": "", "references": [ "GUID:a386fc7f936fef84b8c99b8a1802f3b6", diff --git a/Assets/uni-csv/Tests/Editor/Sov3rain.Unity-CSV.Editor.Tests.asmdef.meta b/Assets/uni-csv/Tests/Editor/Sov3rain.UniCSV.Editor.Tests.asmdef.meta similarity index 100% rename from Assets/uni-csv/Tests/Editor/Sov3rain.Unity-CSV.Editor.Tests.asmdef.meta rename to Assets/uni-csv/Tests/Editor/Sov3rain.UniCSV.Editor.Tests.asmdef.meta diff --git a/Assets/uni-csv/package.json b/Assets/uni-csv/package.json index e7ceb52..e15835a 100644 --- a/Assets/uni-csv/package.json +++ b/Assets/uni-csv/package.json @@ -1,6 +1,6 @@ { "name": "com.sov3rain.uni-csv", - "version": "1.1.0", + "version": "1.1.1", "displayName": "Uni-CSV", "description": "Lightweight and efficient CSV parser without dependencies.", "unity": "2019.2", diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f822d..0920ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.1] - 2024-10-06 + +### Breaking changes + +- Renamed the base namespace to `UniCSV`. + +- Renamed the primary static class to `CsvParser`. + +## Changed + +- Moved some parts of the code to their own utility classes and files. + ## [1.1.0] - 2024-10-06 ### Added diff --git a/README.md b/README.md index d0eddac..031a966 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Returns CSV data as `List>`. You can parse a string using: ```c# -CSVParser.ParseFromString( +CsvParser.ParseFromString( string data, bool header = true, Delimiter delimiter = Delimiter.Auto) @@ -40,7 +40,7 @@ CSVParser.ParseFromString( or a file using: ```c# -CSVParser.ParseFromPath( +CsvParser.ParseFromPath( string path, Delimiter delimiter = Delimiter.Auto, bool header = true, @@ -54,13 +54,13 @@ Both methods have the `header` parameter set to `true` by default. If your CSV f You can map your CSV to a concrete type using generic methods, which will return an `IEnumerator`. Keep in mind that for the mapping to work properly, the input string or file **must include a header row** when using these generic methods. ```c# -CSVParser.ParseFromString( +CsvParser.ParseFromString( string data,     Delimiter delimiter = Delimiter.Auto) ``` ```c# -CSVParser.ParseFromPath( +CsvParser.ParseFromPath( string path,     Delimiter delimiter = Delimiter.Auto,     Encoding = null) @@ -73,7 +73,7 @@ CSVParser.ParseFromPath( Getting back a `List>`: ```c# -var sheet = CSVParser.ParseFromString(csvString); +var sheet = CsvParser.ParseFromString(csvString); foreach (var row in sheet) { @@ -89,7 +89,7 @@ class User public string Username { get; set; } } -var users = CSVParser.ParseFromString(csvString); +var users = CsvParser.ParseFromString(csvString); foreach (User user in users) { @@ -106,7 +106,7 @@ class User public string Username { get; set; } } -var users = CSVParser.ParseFromString(csvString); +var users = CsvParser.ParseFromString(csvString); ``` ## Specs