From e8809b49c403b2505ef479117a2ec797e4b91828 Mon Sep 17 00:00:00 2001 From: Artpupser Date: Sun, 12 Apr 2026 19:12:30 +0300 Subject: [PATCH 1/9] fix test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 71e74f7..656d8ac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: Deploy to nuget +name: Testing on: push: From 9d2a7a8ef0dcb76fa9e635a1f1eb3a904ed17764 Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 18:23:49 +0300 Subject: [PATCH 2/9] added: Option logic --- src/PupaLib.FileIO/PupaLib.FileIO.csproj | 5 ++ src/PupaLib.FileIO/packages.lock.json | 13 +++++ src/PupaLib.FileIO/src/VirtualFile.cs | 65 +++++++++++++++++------- src/PupaLib.FileIO/src/VirtualFolder.cs | 31 +++++++---- src/PupaLib.FileIO/src/VirtualIo.cs | 9 +++- 5 files changed, 92 insertions(+), 31 deletions(-) create mode 100644 src/PupaLib.FileIO/packages.lock.json diff --git a/src/PupaLib.FileIO/PupaLib.FileIO.csproj b/src/PupaLib.FileIO/PupaLib.FileIO.csproj index 179d82f..93d7699 100644 --- a/src/PupaLib.FileIO/PupaLib.FileIO.csproj +++ b/src/PupaLib.FileIO/PupaLib.FileIO.csproj @@ -16,9 +16,14 @@ README.md File, Folder, Directory, Create, Delete, Change, Simple, Lib https://github.com/Artpupser/PupaLib.FileIO.git + true + + + + diff --git a/src/PupaLib.FileIO/packages.lock.json b/src/PupaLib.FileIO/packages.lock.json new file mode 100644 index 0000000..139b665 --- /dev/null +++ b/src/PupaLib.FileIO/packages.lock.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "PupaLib.Core": { + "type": "Direct", + "requested": "[0.0.2, )", + "resolved": "0.0.2", + "contentHash": "CiGPiRbkUOaJD7Rr5pH2cDeF9kXr10AxqlsKNl5zJ+W6Mv39O0ri3FV0Oy+rNEqSTRcnZrNk5ummC1T+Ryjo7Q==" + } + } + } +} \ No newline at end of file diff --git a/src/PupaLib.FileIO/src/VirtualFile.cs b/src/PupaLib.FileIO/src/VirtualFile.cs index 70814ab..9a760b4 100644 --- a/src/PupaLib.FileIO/src/VirtualFile.cs +++ b/src/PupaLib.FileIO/src/VirtualFile.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; +using PupaLib.Core; using PupaLib.FileIO.Serializers; namespace PupaLib.FileIO; @@ -16,7 +17,7 @@ private VirtualFile(string path) { public string Name => _info.Name; - public VirtualFolder? Parent => VirtualFolder.GetFolder(Path.GetDirectoryName(MyPath)); + public Option Parent => VirtualFolder.GetFolder(Path.GetDirectoryName(MyPath)); public string NameWithoutExtension => Path.GetFileNameWithoutExtension(MyPath); @@ -56,20 +57,37 @@ public async Task WriteStringAsync(string content, CancellationToken cancellatio await File.WriteAllTextAsync(MyPath, content, cancellationToken); } - public byte[] ReadBytes() { - return File.ReadAllBytes(MyPath); + public Option ReadBytes() { + try { + return Option.Ok(File.ReadAllBytes(MyPath)); + } catch { + return Option.Fail(); + } } - public async Task ReadBytesAsync(CancellationToken cancellationToken = default) { - return await File.ReadAllBytesAsync(MyPath, cancellationToken); + public async Task> ReadBytesAsync(CancellationToken cancellationToken = default) { + try { + var bytes = await File.ReadAllBytesAsync(MyPath, cancellationToken); + return Option.Ok(bytes); + } catch { + return Option.Fail(); + } } - public string ReadString() { - return File.ReadAllText(MyPath); + public Option ReadString() { + try { + return Option.Ok(File.ReadAllText(MyPath)); + } catch { + return Option.Fail(); + } } - public async Task ReadStringAsync(CancellationToken cancellationToken = default) { - return await File.ReadAllTextAsync(MyPath, cancellationToken); + public async Task> ReadStringAsync(CancellationToken cancellationToken = default) { + try { + return Option.Ok(await File.ReadAllTextAsync(MyPath, cancellationToken)); + } catch { + return Option.Fail(); + } } public void WriteTContent(T content, ISerializer serializer) where T : class { @@ -84,27 +102,38 @@ public async Task WriteTContentAsync(T content, ISerializer serializer, } - public T ReadTContent(ISerializer serializer) where T : class { - var bytes = ReadBytes(); - return serializer.Deserialize(bytes); + public Option ReadTContent(ISerializer serializer) where T : class { + try { + var bytesOption = ReadBytes(); + return !bytesOption.Out(out var bytes) ? Option.Fail() : Option.Ok(serializer.Deserialize(bytes)); + } catch { + return Option.Fail(); + } } - public async Task ReadTContentAsync(ISerializer serializer, CancellationToken cancellationToken = default) + public async Task> ReadTContentAsync(ISerializer serializer, + CancellationToken cancellationToken = default) where T : class { - var bytes = await ReadBytesAsync(cancellationToken); - return serializer.Deserialize(bytes); + try { + var bytesOption = await ReadBytesAsync(cancellationToken); + return !bytesOption.Out(out var bytes) ? Option.Fail() : Option.Ok(serializer.Deserialize(bytes)); + } catch { + return Option.Fail(); + } } #endregion #region STATIC - public static FileNotFoundException GetNotFoundException(string path) { + public static FileNotFoundException NotFoundException(string path) { return new FileNotFoundException($"File not found", path); } - public static VirtualFile? GetFile(string path) { - return File.Exists(path) ? Cache.GetOrAdd(path, x => new VirtualFile(x)) : null; + public static Option GetFile(string path) { + return !File.Exists(path) + ? Option.Fail() + : Option.Ok(Cache.GetOrAdd(path, x => new VirtualFile(x))); } public static VirtualFile GetOrCreate(string path) { diff --git a/src/PupaLib.FileIO/src/VirtualFolder.cs b/src/PupaLib.FileIO/src/VirtualFolder.cs index cc16d25..ed14358 100644 --- a/src/PupaLib.FileIO/src/VirtualFolder.cs +++ b/src/PupaLib.FileIO/src/VirtualFolder.cs @@ -1,5 +1,7 @@ using System.Collections.Concurrent; +using PupaLib.Core; + namespace PupaLib.FileIO; public class VirtualFolder { @@ -12,7 +14,7 @@ private VirtualFolder(string path) { public string MyPath => _info.FullName; - public VirtualFolder? Parent => GetFolder(_info.Parent?.FullName!); + public Option Parent => GetFolder(_info.Parent?.FullName!); public string Name => _info.Name; @@ -40,7 +42,7 @@ public VirtualFolder GetOrCreateFolderIn(string pathIn) { return GetOrCreateFolder(BuildPath(pathIn)); } - public VirtualFolder? GetFolderIn(string pathIn) { + public Option GetFolderIn(string pathIn) { return GetFolder(BuildPath(pathIn)); } @@ -48,19 +50,24 @@ public VirtualFile GetOrCreateFileIn(string pathIn) { return VirtualFile.GetOrCreate(BuildPath(pathIn)); } - public VirtualFile? GetFileIn(string pathIn) { + public Option GetFileIn(string pathIn) { return VirtualFile.GetFile(BuildPath(pathIn)); } public IEnumerable EnumerateFiles(SearchOption option) { - return _info.EnumerateFiles("*", option) - .Select(x => VirtualFile.GetFile(x.FullName)) - .Where(x => x != null)!; + var contents = new List(64); + foreach (var fileInfo in _info.EnumerateFiles("*", option)) + if (VirtualFile.GetFile(fileInfo.FullName) is { Success: true } file) + contents.Add(file.Content); + return contents; } public IEnumerable EnumerateFolders(SearchOption option) { - return _info.EnumerateDirectories("*", option) - .Select(x => GetFolder(x.FullName)!); + var contents = new List(64); + foreach (var directoryInfo in _info.EnumerateDirectories("*", option)) + if (GetFolder(directoryInfo.FullName) is { Success: true } folder) + contents.Add(folder.Content); + return contents; } public int GetDirectoriesCount(SearchOption option = SearchOption.TopDirectoryOnly) { @@ -81,12 +88,14 @@ public void DeleteMe(bool full = true) { #region STATIC - public static DirectoryNotFoundException GetNotFoundException(string path) { + public static DirectoryNotFoundException NotFoundException(string path) { return new DirectoryNotFoundException($"Directory not found {path}"); } - public static VirtualFolder? GetFolder(string? path) { - return Directory.Exists(path) ? Cache.GetOrAdd(path, s => new VirtualFolder(s)) : null; + public static Option GetFolder(string? path) { + return Directory.Exists(path) + ? Option.Ok(Cache.GetOrAdd(path, s => new VirtualFolder(s))) + : Option.Fail(); } public static VirtualFolder GetOrCreateFolder(string path) { diff --git a/src/PupaLib.FileIO/src/VirtualIo.cs b/src/PupaLib.FileIO/src/VirtualIo.cs index 94d0a46..e9ca0ef 100644 --- a/src/PupaLib.FileIO/src/VirtualIo.cs +++ b/src/PupaLib.FileIO/src/VirtualIo.cs @@ -14,6 +14,11 @@ public static class VirtualIo { /// Automatically initialized from . /// Throws if the directory is not accessible /// - public static readonly VirtualFolder RootFolder = - VirtualFolder.GetFolder(RootPath) ?? throw VirtualFolder.GetNotFoundException(RootPath); + public static readonly VirtualFolder RootFolder; + + static VirtualIo() { + var optionFolder = VirtualFolder.GetFolder(RootPath); + if (!optionFolder.Out(out var folder)) throw VirtualFolder.NotFoundException(RootPath); + RootFolder = folder; + } } \ No newline at end of file From 1b6baa2da7e7c0a6ddc911992809b177b9537fbb Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 18:29:16 +0300 Subject: [PATCH 3/9] fix: rewriting example on new check's system --- README.md | 11 +- .../PupaLib.FileIO.Example.csproj | 1 + src/PupaLib.FileIO.Example/packages.lock.json | 18 +++ src/PupaLib.FileIO.Example/src/ExampleApp.cs | 20 +-- .../PupaLib.FileIO.Tests.csproj | 2 +- src/PupaLib.FileIO.Tests/packages.lock.json | 141 ++++++++++++++++++ 6 files changed, 180 insertions(+), 13 deletions(-) create mode 100644 src/PupaLib.FileIO.Example/packages.lock.json create mode 100644 src/PupaLib.FileIO.Tests/packages.lock.json diff --git a/README.md b/README.md index c5989e7..b298842 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ ![NuGet](https://img.shields.io/nuget/v/PupaLib.FileIO.svg?style=for-the-badge) ![.NET](https://img.shields.io/badge/.NET-10.0-blue?style=for-the-badge) - #### [PupaLib.FileIO](https://www.nuget.org/packages/PupaLib.FileIO) is an incredibly simple library for working with files and folders. 🎯 @@ -19,6 +18,7 @@ ## 📜 Content + - [🗝️ Key Features](#-key-features) - [🚀️ Installation](#-installation) - [🗃️ Devlog](#-devlog) @@ -147,8 +147,15 @@ if(folder is not null) { ## 🗃️ Devlog +### 1.2.2 + +- Added: field 'RestorePackagesWithLockFile' in 'PropertyGroup' -> *.csproj +- Added: PupaLib.Core +- Changes: Operations through Option + ### 1.2.1 -- Changed .NET SDK version, .net8.0 -> .net10.0 + +- Changed .NET SDK version, .net8.0 -> .net10.0 ## ⚖️ License diff --git a/src/PupaLib.FileIO.Example/PupaLib.FileIO.Example.csproj b/src/PupaLib.FileIO.Example/PupaLib.FileIO.Example.csproj index 9606702..1b7fe1f 100644 --- a/src/PupaLib.FileIO.Example/PupaLib.FileIO.Example.csproj +++ b/src/PupaLib.FileIO.Example/PupaLib.FileIO.Example.csproj @@ -4,6 +4,7 @@ net10.0 disable enable + true diff --git a/src/PupaLib.FileIO.Example/packages.lock.json b/src/PupaLib.FileIO.Example/packages.lock.json new file mode 100644 index 0000000..2e5629d --- /dev/null +++ b/src/PupaLib.FileIO.Example/packages.lock.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "PupaLib.Core": { + "type": "Transitive", + "resolved": "0.0.2", + "contentHash": "CiGPiRbkUOaJD7Rr5pH2cDeF9kXr10AxqlsKNl5zJ+W6Mv39O0ri3FV0Oy+rNEqSTRcnZrNk5ummC1T+Ryjo7Q==" + }, + "pupalib.fileio": { + "type": "Project", + "dependencies": { + "PupaLib.Core": "[0.0.2, )" + } + } + } + } +} \ No newline at end of file diff --git a/src/PupaLib.FileIO.Example/src/ExampleApp.cs b/src/PupaLib.FileIO.Example/src/ExampleApp.cs index 4d4c197..a747637 100644 --- a/src/PupaLib.FileIO.Example/src/ExampleApp.cs +++ b/src/PupaLib.FileIO.Example/src/ExampleApp.cs @@ -50,8 +50,8 @@ public static void Example1() { /// Loads existing file or shows null if not found. /// public static async Task Example2(CancellationToken cancellationToken) { - var file = VirtualIo.RootFolder.GetFileIn(ExampleFileName); - if (file is not null) + var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); + if (fileOption.Out(out var file)) Console.Write($"{file.GetInfo()}\n"); await Task.CompletedTask; } @@ -60,8 +60,8 @@ public static async Task Example2(CancellationToken cancellationToken) { /// Writes and reads plain text content to/from file. /// public static async Task Example3_1(CancellationToken cancellationToken) { - var file = VirtualIo.RootFolder.GetFileIn(ExampleFileName); - if (file is not null) { + var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); + if (fileOption.Out(out var file)) { const string content = "Hello world!"; await file.WriteStringAsync(content, cancellationToken); Console.Write( @@ -75,8 +75,8 @@ public static async Task Example3_1(CancellationToken cancellationToken) { /// Serializes and deserializes object to/from JSON file. /// public static async Task Example3_2(CancellationToken cancellationToken) { - var file = VirtualIo.RootFolder.GetFileIn(ExampleFileName); - if (file is not null) { + var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); + if (fileOption.Out(out var file)) { var content = new ExampleObject("Name", "Lastname", [12, 53, 47]); await file.WriteTContentAsync(content, new JsonSystemSerializer(), cancellationToken); Console.Write( @@ -90,8 +90,8 @@ public static async Task Example3_2(CancellationToken cancellationToken) { /// Deletes the example file. /// public static void Example4() { - var file = VirtualIo.RootFolder.GetFileIn(ExampleFileName); - if (file is null) return; + var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); + if (!fileOption.Out(out var file)) return; file.DeleteMe(); Console.Write($"File deleted, exists: {file.Exists}\n"); } @@ -108,8 +108,8 @@ public static void Example5() { /// Deletes the example folder. /// public static void Example6() { - var folder = VirtualIo.RootFolder.GetFolderIn(ExampleFolderName); - if (folder is null) return; + var folderOption = VirtualIo.RootFolder.GetFolderIn(ExampleFolderName); + if (!folderOption.Out(out var folder)) return; folder.DeleteMe(); Console.Write($"Folder deleted, exists: {folder.Exists}\n"); } diff --git a/src/PupaLib.FileIO.Tests/PupaLib.FileIO.Tests.csproj b/src/PupaLib.FileIO.Tests/PupaLib.FileIO.Tests.csproj index b5dcb7b..6caf434 100644 --- a/src/PupaLib.FileIO.Tests/PupaLib.FileIO.Tests.csproj +++ b/src/PupaLib.FileIO.Tests/PupaLib.FileIO.Tests.csproj @@ -4,7 +4,7 @@ net10.0 enable enable - + true false true diff --git a/src/PupaLib.FileIO.Tests/packages.lock.json b/src/PupaLib.FileIO.Tests/packages.lock.json new file mode 100644 index 0000000..56cb426 --- /dev/null +++ b/src/PupaLib.FileIO.Tests/packages.lock.json @@ -0,0 +1,141 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.8.0, )", + "resolved": "17.8.0", + "contentHash": "BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==", + "dependencies": { + "Microsoft.CodeCoverage": "17.8.0", + "Microsoft.TestPlatform.TestHost": "17.8.0" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.5.3, )", + "resolved": "2.5.3", + "contentHash": "VxYDiWSwrLxOJ3UEN+ZPrBybB0SFShQ1E6PjT65VdoKCJhorgerFznThjSwawRH/WAip73YnucDVsE8WRj/8KQ==", + "dependencies": { + "xunit.analyzers": "1.4.0", + "xunit.assert": "2.5.3", + "xunit.core": "[2.5.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.5.3, )", + "resolved": "2.5.3", + "contentHash": "HFFL6O+QLEOfs555SqHii48ovVa4CqGYanY+B32BjLpPptdE+wEJmCFNXlLHdEOD5LYeayb9EroaUpydGpcybg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==", + "dependencies": { + "NuGet.Frameworks": "6.5.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.8.0", + "contentHash": "9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.8.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "6.5.0", + "contentHash": "QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==" + }, + "PupaLib.Core": { + "type": "Transitive", + "resolved": "0.0.2", + "contentHash": "CiGPiRbkUOaJD7Rr5pH2cDeF9kXr10AxqlsKNl5zJ+W6Mv39O0ri3FV0Oy+rNEqSTRcnZrNk5ummC1T+Ryjo7Q==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.4.0", + "contentHash": "7ljnTJfFjz5zK+Jf0h2dd2QOSO6UmFizXsojv/x4QX7TU5vEgtKZPk9RvpkiuUqg2bddtNZufBoKQalsi7djfA==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.5.3", + "contentHash": "MK3HiBckO3vdxEdUxXZyyRPsBNPsC/nz6y1gj/UZIZkjMnsVQyZPU8yxS/3cjTchYcqskt/nqUOS5wmD8JezdQ==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.5.3", + "contentHash": "FE8yEEUkoMLd6kOHDXm/QYfX/dYzwc0c+Q4MQon6VGRwFuy6UVGwK/CFA5LEea+ZBEmcco7AEl2q78VjsA0j/w==", + "dependencies": { + "xunit.extensibility.core": "[2.5.3]", + "xunit.extensibility.execution": "[2.5.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.5.3", + "contentHash": "IjAQlPeZWXP89pl1EuOG9991GH1qgAL0rQfkmX2UV+PDenbYb7oBnQopL9ujE6YaXxgaQazp7lFjsDyyxD6Mtw==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.5.3", + "contentHash": "w9eGCHl+gJj1GzZSf0VTzYPp/gv4fiUDkr+yR7/Wv9/ucO2CHltGg2TnyySLFjzekkjuxVJZUE+tZyDNzryJFw==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "xunit.extensibility.core": "[2.5.3]" + } + }, + "pupalib.fileio": { + "type": "Project", + "dependencies": { + "PupaLib.Core": "[0.0.2, )" + } + } + } + } +} \ No newline at end of file From d6cc157362441372f14b0cd4a0dd54777303cf45 Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 18:40:26 +0300 Subject: [PATCH 4/9] fix: added Option result on all loads methods File/Folder --- src/PupaLib.FileIO.Example/src/ExampleApp.cs | 14 ++++++--- .../src/Models/TestObjectFirst.cs | 16 ++++------ .../src/Models/TestObjectSecond.cs | 13 +++------ .../src/Tests/FilesTest.cs | 29 +++++++++---------- src/PupaLib.FileIO/src/VirtualFile.cs | 19 +++++++----- src/PupaLib.FileIO/src/VirtualFolder.cs | 23 +++++++++------ 6 files changed, 59 insertions(+), 55 deletions(-) diff --git a/src/PupaLib.FileIO.Example/src/ExampleApp.cs b/src/PupaLib.FileIO.Example/src/ExampleApp.cs index a747637..8c88edb 100644 --- a/src/PupaLib.FileIO.Example/src/ExampleApp.cs +++ b/src/PupaLib.FileIO.Example/src/ExampleApp.cs @@ -42,7 +42,9 @@ public static async Task Main() { /// Creates a file if it doesn't exist and shows file info. /// public static void Example1() { - var file = VirtualIo.RootFolder.GetOrCreateFileIn(ExampleFileName); + var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(ExampleFileName); + if (!fileOption.Out(out var file)) + return; Console.Write($"{file.GetInfo()}\n"); } @@ -91,7 +93,8 @@ public static async Task Example3_2(CancellationToken cancellationToken) { /// public static void Example4() { var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); - if (!fileOption.Out(out var file)) return; + if (!fileOption.Out(out var file)) + return; file.DeleteMe(); Console.Write($"File deleted, exists: {file.Exists}\n"); } @@ -100,7 +103,9 @@ public static void Example4() { /// Creates example folder if it doesn't exist. /// public static void Example5() { - var folder = VirtualIo.RootFolder.GetOrCreateFolderIn(ExampleFolderName); + var folderOption = VirtualIo.RootFolder.GetOrCreateFolderIn(ExampleFolderName); + if (!folderOption.Out(out var folder)) + return; Console.Write($"{folder.GetInfo()}\n"); } @@ -109,7 +114,8 @@ public static void Example5() { /// public static void Example6() { var folderOption = VirtualIo.RootFolder.GetFolderIn(ExampleFolderName); - if (!folderOption.Out(out var folder)) return; + if (!folderOption.Out(out var folder)) + return; folder.DeleteMe(); Console.Write($"Folder deleted, exists: {folder.Exists}\n"); } diff --git a/src/PupaLib.FileIO.Tests/src/Models/TestObjectFirst.cs b/src/PupaLib.FileIO.Tests/src/Models/TestObjectFirst.cs index 1be67bc..5bfe306 100644 --- a/src/PupaLib.FileIO.Tests/src/Models/TestObjectFirst.cs +++ b/src/PupaLib.FileIO.Tests/src/Models/TestObjectFirst.cs @@ -3,15 +3,9 @@ namespace PupaLib.FileIO.Tests.Models; [Serializable] -public sealed class TestObjectFirst { - [JsonConstructor] - public TestObjectFirst(string name, List numbers, string lastname) { - Name = name; - Numbers = numbers; - LastName = lastname; - } - - [JsonPropertyName("Name")] public string Name { get; } - [JsonPropertyName("Numbers")] public List Numbers { get; } - [JsonPropertyName("Lastname")] public string LastName { get; } +[method: JsonConstructor] +public sealed class TestObjectFirst(string name, List numbers, string lastname) { + [JsonPropertyName("Name")] public string Name { get; } = name; + [JsonPropertyName("Numbers")] public List Numbers { get; } = numbers; + [JsonPropertyName("Lastname")] public string LastName { get; } = lastname; } \ No newline at end of file diff --git a/src/PupaLib.FileIO.Tests/src/Models/TestObjectSecond.cs b/src/PupaLib.FileIO.Tests/src/Models/TestObjectSecond.cs index 524e772..9df2b0d 100644 --- a/src/PupaLib.FileIO.Tests/src/Models/TestObjectSecond.cs +++ b/src/PupaLib.FileIO.Tests/src/Models/TestObjectSecond.cs @@ -3,13 +3,8 @@ namespace PupaLib.FileIO.Tests.Models; [Serializable] -public sealed class TestObjectSecond { - [JsonConstructor] - public TestObjectSecond(string name, string lastname) { - Name = name; - LastName = lastname; - } - - [JsonPropertyName("Name")] public string Name { get; } - [JsonPropertyName("Lastname")] public string LastName { get; } +[method: JsonConstructor] +public sealed class TestObjectSecond(string name, string lastname) { + [JsonPropertyName("Name")] public string Name { get; } = name; + [JsonPropertyName("Lastname")] public string LastName { get; } = lastname; } \ No newline at end of file diff --git a/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs b/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs index a73d157..7719a20 100644 --- a/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs +++ b/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs @@ -5,23 +5,20 @@ namespace PupaLib.FileIO.Tests.Tests; [CollectionDefinition("FileTests collection", DisableParallelization = true)] public class FilesTest { - public const string FileName = "test_file.txt"; + private const string FileName = "test_file.txt"; - private static bool FileIsCorrect(VirtualFile? file) { - return file != null && file.Exists && file.Name != string.Empty; - } [Fact(DisplayName = "Creating file")] public void Should_CreateFile_When_ValidPath() { var file = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - Assert.True(FileIsCorrect(file), "File was not correct"); + Assert.True(file.Exists, "File was not correct"); } [Fact(DisplayName = "Load file")] public void Should_LoadFile_When_ValidPathAndAlreadyCreated() { VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - var file = VirtualIo.RootFolder.GetFileIn(FileName); - Assert.True(FileIsCorrect(file), "File was not correct"); + var fileOption = VirtualIo.RootFolder.GetFileIn(FileName); + Assert.True(fileOption, "File was not correct"); } [Theory(DisplayName = "Write and read file")] @@ -30,13 +27,16 @@ public void Should_LoadFile_When_ValidPathAndAlreadyCreated() { [InlineData("Happy me!")] public void Should_WriteAndReadFile_When_ValidPathAndAlreadyCreated(string content) { VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - var file = VirtualIo.RootFolder.GetFileIn(FileName); - var fileCorrect = FileIsCorrect(file); - Assert.True(fileCorrect, "File was not correct"); - if (!fileCorrect) return; + var fileOption = VirtualIo.RootFolder.GetFileIn(FileName); + Assert.True(fileOption, "File was not correct"); + if (!fileOption.Out(out var file)) + return; file!.WriteString(content); - var reads = file.ReadString(); - Assert.True(reads == content, $"File is empty, {reads} != {content}"); + var readOperationOption = file.ReadString(); + Assert.True(readOperationOption, $"Reading operation is bad"); + if (!readOperationOption.Out(out var str)) + return; + Assert.True(str == content, $"File is empty, {readOperationOption} != {content}"); } [Theory(DisplayName = "Write and read file")] @@ -45,8 +45,7 @@ public void Should_WriteAndReadFile_When_ValidPathAndAlreadyCreated(string conte [InlineData("name3", "lastname3")] public void Should_WriteAndReadFileT_When_ValidPathAndAlreadyCreated(string name, string lastname) { - var file = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - var fileCorrect = FileIsCorrect(file); + var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); Assert.True(fileCorrect, "File was not correct"); if (!fileCorrect) return; var content = new TestObjectSecond(name, lastname); diff --git a/src/PupaLib.FileIO/src/VirtualFile.cs b/src/PupaLib.FileIO/src/VirtualFile.cs index 9a760b4..1f77b6e 100644 --- a/src/PupaLib.FileIO/src/VirtualFile.cs +++ b/src/PupaLib.FileIO/src/VirtualFile.cs @@ -136,15 +136,20 @@ public static Option GetFile(string path) { : Option.Ok(Cache.GetOrAdd(path, x => new VirtualFile(x))); } - public static VirtualFile GetOrCreate(string path) { - return Cache.GetOrAdd(path, x => File.Exists(x) ? new VirtualFile(path) : CreateFile(path)); + public static Option GetOrCreate(string path) { + return Option.Ok(Cache.GetOrAdd(path, + x => File.Exists(x) ? new VirtualFile(path) : CreateFile(path).Content)); } - private static VirtualFile CreateFile(string path) { - File.Create(path).Dispose(); - var value = new VirtualFile(path); - Cache.TryAdd(path, value); - return value; + private static Option CreateFile(string path) { + try { + File.Create(path).Dispose(); + var value = new VirtualFile(path); + Cache.TryAdd(path, value); + return Option.Ok(value); + } catch { + return Option.Fail(); + } } #endregion diff --git a/src/PupaLib.FileIO/src/VirtualFolder.cs b/src/PupaLib.FileIO/src/VirtualFolder.cs index ed14358..6620152 100644 --- a/src/PupaLib.FileIO/src/VirtualFolder.cs +++ b/src/PupaLib.FileIO/src/VirtualFolder.cs @@ -38,7 +38,7 @@ public string BuildPath(string pathIn) { } - public VirtualFolder GetOrCreateFolderIn(string pathIn) { + public Option GetOrCreateFolderIn(string pathIn) { return GetOrCreateFolder(BuildPath(pathIn)); } @@ -46,7 +46,7 @@ public Option GetFolderIn(string pathIn) { return GetFolder(BuildPath(pathIn)); } - public VirtualFile GetOrCreateFileIn(string pathIn) { + public Option GetOrCreateFileIn(string pathIn) { return VirtualFile.GetOrCreate(BuildPath(pathIn)); } @@ -98,15 +98,20 @@ public static Option GetFolder(string? path) { : Option.Fail(); } - public static VirtualFolder GetOrCreateFolder(string path) { - return Cache.GetOrAdd(path, s => Directory.Exists(s) ? new VirtualFolder(path) : CreateFolder(path)); + public static Option GetOrCreateFolder(string path) { + return Option.Ok(Cache.GetOrAdd(path, + keyPath => Directory.Exists(keyPath) ? new VirtualFolder(path) : CreateFolder(path).Content)); } - private static VirtualFolder CreateFolder(string path) { - Directory.CreateDirectory(path); - var value = new VirtualFolder(path); - Cache.TryAdd(path, value); - return value; + private static Option CreateFolder(string path) { + try { + Directory.CreateDirectory(path); + var value = new VirtualFolder(path); + Cache.TryAdd(path, value); + return Option.Ok(value); + } catch (Exception e) { + return Option.Fail(); + } } #endregion From 8f3225b1e8a75b5808a811800cc0ee265f8dc58c Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 18:47:45 +0300 Subject: [PATCH 5/9] changes: rewrite tests on new check's system Option --- src/PupaLib.FileIO.Example/src/ExampleApp.cs | 12 +++--- .../src/Tests/FileTestAsync.cs | 35 +++++++++--------- .../src/Tests/FilesTest.cs | 33 ++++++++++------- .../src/Tests/FoldersTest.cs | 37 ++++++++++--------- 4 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/PupaLib.FileIO.Example/src/ExampleApp.cs b/src/PupaLib.FileIO.Example/src/ExampleApp.cs index 8c88edb..ee1af64 100644 --- a/src/PupaLib.FileIO.Example/src/ExampleApp.cs +++ b/src/PupaLib.FileIO.Example/src/ExampleApp.cs @@ -15,12 +15,12 @@ public static class ExampleApp { /// /// Sample filename used in examples: "file.txt". /// - public const string ExampleFileName = "file.txt"; + private const string ExampleFileName = "file.txt"; /// /// Sample folder name used in examples: "folder". /// - public const string ExampleFolderName = "folder"; + private const string ExampleFolderName = "folder"; /// /// Runs all FileIO examples sequentially. @@ -41,7 +41,7 @@ public static async Task Main() { /// /// Creates a file if it doesn't exist and shows file info. /// - public static void Example1() { + public static void Example1(CancellationToken cancellationToken = default) { var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(ExampleFileName); if (!fileOption.Out(out var file)) return; @@ -51,7 +51,7 @@ public static void Example1() { /// /// Loads existing file or shows null if not found. /// - public static async Task Example2(CancellationToken cancellationToken) { + public static async Task Example2(CancellationToken cancellationToken = default) { var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); if (fileOption.Out(out var file)) Console.Write($"{file.GetInfo()}\n"); @@ -61,7 +61,7 @@ public static async Task Example2(CancellationToken cancellationToken) { /// /// Writes and reads plain text content to/from file. /// - public static async Task Example3_1(CancellationToken cancellationToken) { + public static async Task Example3_1(CancellationToken cancellationToken = default) { var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); if (fileOption.Out(out var file)) { const string content = "Hello world!"; @@ -76,7 +76,7 @@ public static async Task Example3_1(CancellationToken cancellationToken) { /// /// Serializes and deserializes object to/from JSON file. /// - public static async Task Example3_2(CancellationToken cancellationToken) { + public static async Task Example3_2(CancellationToken cancellationToken = default) { var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); if (fileOption.Out(out var file)) { var content = new ExampleObject("Name", "Lastname", [12, 53, 47]); diff --git a/src/PupaLib.FileIO.Tests/src/Tests/FileTestAsync.cs b/src/PupaLib.FileIO.Tests/src/Tests/FileTestAsync.cs index 30eb739..3bcb3a1 100644 --- a/src/PupaLib.FileIO.Tests/src/Tests/FileTestAsync.cs +++ b/src/PupaLib.FileIO.Tests/src/Tests/FileTestAsync.cs @@ -5,11 +5,7 @@ namespace PupaLib.FileIO.Tests.Tests; [CollectionDefinition("FileTestsAsync collection", DisableParallelization = true)] public class FileTestAsync { - public const string FileName = "test_file_async.txt"; - - private static bool FileIsCorrect(VirtualFile? file) { - return file != null && file.Exists && file.Name != string.Empty; - } + private const string FileName = "test_file_async.txt"; [Theory(DisplayName = "Write and read file")] [InlineData("Hello world!")] @@ -17,12 +13,13 @@ private static bool FileIsCorrect(VirtualFile? file) { [InlineData("Happy me!")] public async Task Should_WriteAndReadFile_When_ValidPathAndAlreadyCreated(string content, CancellationToken cancellationToken = default) { - var file = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - var fileCorrect = FileIsCorrect(file); - Assert.True(fileCorrect, "File was not correct"); - if (!fileCorrect) return; - await file!.WriteStringAsync(content, cancellationToken); - var reads = await file.ReadStringAsync(cancellationToken); + var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); + Assert.True(fileOption, "File was not correct"); + if (!fileOption.Out(out var file)) return; + await file.WriteStringAsync(content, cancellationToken); + var readOperationOption = await file.ReadStringAsync(cancellationToken); + if (!readOperationOption.Out(out var reads)) + return; Assert.True(reads == content, $"File is empty, {reads} != {content}"); } @@ -33,13 +30,17 @@ public async Task Should_WriteAndReadFile_When_ValidPathAndAlreadyCreated(string public async Task Should_WriteAndReadFileT_When_ValidPathAndAlreadyCreated(string name, string lastname, CancellationToken cancellationToken = default) { - var file = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - var fileCorrect = FileIsCorrect(file); - Assert.True(fileCorrect, "File was not correct"); - if (!fileCorrect) return; + var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); + Assert.True(fileOption, "File was not correct"); + if (!fileOption.Out(out var file)) return; var content = new TestObjectSecond(name, lastname); - await file!.WriteTContentAsync(content, new JsonSystemSerializer(), cancellationToken); - var reads = await file.ReadTContentAsync(new JsonSystemSerializer(), cancellationToken); + await file.WriteTContentAsync(content, new JsonSystemSerializer(), cancellationToken); + var readOperationOption = + await file.ReadTContentAsync(new JsonSystemSerializer(), cancellationToken); + Assert.True(readOperationOption, + "Read T content failed."); + if (!readOperationOption.Out(out var reads)) + return; Assert.True(reads.Name == content.Name && reads.LastName == content.LastName, $"File is empty, {reads} != {content}"); } diff --git a/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs b/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs index 7719a20..9eb324b 100644 --- a/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs +++ b/src/PupaLib.FileIO.Tests/src/Tests/FilesTest.cs @@ -10,8 +10,8 @@ public class FilesTest { [Fact(DisplayName = "Creating file")] public void Should_CreateFile_When_ValidPath() { - var file = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - Assert.True(file.Exists, "File was not correct"); + var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); + Assert.True(fileOption, "File was not correct"); } [Fact(DisplayName = "Load file")] @@ -31,9 +31,9 @@ public void Should_WriteAndReadFile_When_ValidPathAndAlreadyCreated(string conte Assert.True(fileOption, "File was not correct"); if (!fileOption.Out(out var file)) return; - file!.WriteString(content); + file.WriteString(content); var readOperationOption = file.ReadString(); - Assert.True(readOperationOption, $"Reading operation is bad"); + Assert.True(readOperationOption, "Reading operation is bad"); if (!readOperationOption.Out(out var str)) return; Assert.True(str == content, $"File is empty, {readOperationOption} != {content}"); @@ -46,23 +46,28 @@ public void Should_WriteAndReadFile_When_ValidPathAndAlreadyCreated(string conte public void Should_WriteAndReadFileT_When_ValidPathAndAlreadyCreated(string name, string lastname) { var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - Assert.True(fileCorrect, "File was not correct"); - if (!fileCorrect) return; + Assert.True(fileOption, "File was not correct"); + if (!fileOption.Out(out var file)) + return; var content = new TestObjectSecond(name, lastname); - file!.WriteTContent(content, new JsonSystemSerializer()); - var reads = file.ReadTContent(new JsonSystemSerializer()); + file.WriteTContent(content, new JsonSystemSerializer()); + var readOperationOption = file.ReadTContent(new JsonSystemSerializer()); + Assert.True(readOperationOption, + "Read T content failed."); + if (!readOperationOption.Out(out var reads)) + return; Assert.True(reads.Name == content.Name && reads.LastName == content.LastName, - $"File is empty, {reads} != {content}"); + $"File is empty, {readOperationOption} != {content}"); } [Fact(DisplayName = "Delete file")] public void Should_DeleteFile_When_ValidPathAndAlreadyCreated() { VirtualIo.RootFolder.GetOrCreateFileIn(FileName); - var file = VirtualIo.RootFolder.GetFileIn(FileName); - var fileCorrect = FileIsCorrect(file); - Assert.True(fileCorrect); - if (!fileCorrect) return; - file!.DeleteMe(); + var fileOption = VirtualIo.RootFolder.GetFileIn(FileName); + Assert.True(fileOption); + if (!fileOption.Out(out var file)) + return; + file.DeleteMe(); Assert.False(file.Exists); } } \ No newline at end of file diff --git a/src/PupaLib.FileIO.Tests/src/Tests/FoldersTest.cs b/src/PupaLib.FileIO.Tests/src/Tests/FoldersTest.cs index ab5d1d9..aea30b7 100644 --- a/src/PupaLib.FileIO.Tests/src/Tests/FoldersTest.cs +++ b/src/PupaLib.FileIO.Tests/src/Tests/FoldersTest.cs @@ -2,12 +2,8 @@ [CollectionDefinition("FoldersTest collection", DisableParallelization = true)] public class FoldersTest { - public const string FolderName = "test_folder"; - public const string MessageErrorCorrect = "Folder is not correct"; - - private static bool FolderIsCorrect(VirtualFolder? folder) { - return folder != null && folder.Exists && folder.Name != string.Empty; - } + private const string FolderName = "test_folder"; + private const string MessageErrorCorrect = "Folder is not correct"; [Fact(DisplayName = "Root folder is correct")] public void Should_CheckRootFolder_When_FolderIsCorrect() { @@ -17,38 +13,45 @@ public void Should_CheckRootFolder_When_FolderIsCorrect() { [Fact(DisplayName = "Creating folder")] public void Should_CreateFolder_When_ValidPath() { - var folder = VirtualIo.RootFolder.GetOrCreateFolderIn(FolderName); - Assert.True(FolderIsCorrect(folder), MessageErrorCorrect); + var folderOption = VirtualIo.RootFolder.GetOrCreateFolderIn(FolderName); + Assert.True(folderOption, MessageErrorCorrect); } [Fact(DisplayName = "Load folder")] public void Should_LoadFolder_When_ValidPathAndAlreadyCreated() { VirtualIo.RootFolder.GetOrCreateFolderIn(FolderName); - var folder = VirtualIo.RootFolder.GetFolderIn(FolderName); - Assert.True(FolderIsCorrect(folder), MessageErrorCorrect); + var folderOption = VirtualIo.RootFolder.GetFolderIn(FolderName); + Assert.True(folderOption, MessageErrorCorrect); } [Fact(DisplayName = "Fill and removes files")] public void Should_FillFiles_When_ValidPathAndAlreadyCreated() { VirtualIo.RootFolder.GetOrCreateFolderIn(FolderName); - var folder = VirtualIo.RootFolder.GetFolderIn(FolderName); - Assert.True(FolderIsCorrect(folder), MessageErrorCorrect); + var folderOption = VirtualIo.RootFolder.GetFolderIn(FolderName); + Assert.True(folderOption, MessageErrorCorrect); + if (!folderOption.Out(out var folder)) + return; for (var i = 0; i < 5; i++) { - var file = folder!.GetOrCreateFileIn($"file{i}.txt"); + var fileOption = folder.GetOrCreateFileIn($"file{i}.txt"); + Assert.True(fileOption, $"File 'file{i}.txt' read failed"); + if (!fileOption.Out(out var file)) + return; Assert.True(file.Exists); file.DeleteMe(); Assert.False(file.Exists); } - folder!.DeleteMe(); + folder.DeleteMe(); } [Fact(DisplayName = "Delete folder")] public void Should_DeleteFolder_When_ValidPathAndAlreadyCreated() { VirtualIo.RootFolder.GetOrCreateFolderIn(FolderName); - var folder = VirtualIo.RootFolder.GetFolderIn(FolderName); - Assert.True(FolderIsCorrect(folder), MessageErrorCorrect); - folder!.DeleteMe(); + var folderOption = VirtualIo.RootFolder.GetFolderIn(FolderName); + Assert.True(folderOption, MessageErrorCorrect); + if (!folderOption.Out(out var folder)) + return; + folder.DeleteMe(); Assert.False(folder.Exists, $"Folder not deleted, {folder.MyPath}"); } } \ No newline at end of file From fbecc45972ab3ca9e3256d2fa68d4db8ae2224d7 Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 18:57:48 +0300 Subject: [PATCH 6/9] fix: warning in 122 line VirtualFolder --- README.md | 3 ++- src/PupaLib.FileIO/src/VirtualFolder.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b298842..39ff2d5 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,8 @@ if(folder is not null) { - Added: field 'RestorePackagesWithLockFile' in 'PropertyGroup' -> *.csproj - Added: PupaLib.Core -- Changes: Operations through Option +- Changes: New check system through Option used PupaLib.Core +- Rewrite: Usage in readme ### 1.2.1 diff --git a/src/PupaLib.FileIO/src/VirtualFolder.cs b/src/PupaLib.FileIO/src/VirtualFolder.cs index 6620152..15085f9 100644 --- a/src/PupaLib.FileIO/src/VirtualFolder.cs +++ b/src/PupaLib.FileIO/src/VirtualFolder.cs @@ -109,7 +109,7 @@ private static Option CreateFolder(string path) { var value = new VirtualFolder(path); Cache.TryAdd(path, value); return Option.Ok(value); - } catch (Exception e) { + } catch { return Option.Fail(); } } From 62670fe5affd2fde0d40980cf689489ecd68e545 Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 19:48:20 +0300 Subject: [PATCH 7/9] fix: ExampleApp.cs in PupaLib.FileIo.Example --- README.md | 201 ++++++++++++++----- src/PupaLib.FileIO.Example/src/ExampleApp.cs | 6 +- 2 files changed, 152 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 39ff2d5..d68576b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ - [🗝️ Key Features](#-key-features) - [🚀️ Installation](#-installation) +- [💡 Usage](#-usage) - [🗃️ Devlog](#-devlog) - [⚖️️ License](#-license) @@ -49,110 +50,206 @@ Install-Package PupaLib.FileIO ## 💡 Usage -### 🛠️ File or Folder Creation +Here is a practical guide on how to use the `VirtualFile`, `VirtualFolder`, and `VirtualIo` classes to manage your file +system operations efficiently. -Create a file: +### 🛠️ File and Folder Creation + +**Creating a folder:** ```csharp -var path = "./path/file_name.txt"; -var file = VirtualFile.CreateFile(path); +var path = "./data/new_folder"; +var folder = await VirtualFolder.GetOrCreateFolder(path); // Returns Option + +if (folder.Out(out var _folder)) +{ + Console.WriteLine($"Folder created: {_folder.MyPath}"); +} ``` -Create a folder: +**Creating a file:** ```csharp -var path = "./path/folder_name"; -var folder = VirtualFile.CreateFolder(path); +var filePath = "./data/test.txt"; +var file = await VirtualFile.GetOrCreate(filePath); // Returns Option + +if (file.Out(out var _file)) +{ + Console.WriteLine($"File created: {_file.MyPath}"); +} ``` +> **Note:** We use `GetOrCreateFolder` / `GetOrCreate` which handles both existing resources and creates new ones if +> they don't exist. The example above assumes you await the option and unwrap the successful result. If you want to use +`Option`, you can simply check `if (folder) {}`. + ### 🔎 Check If File or Folder Exists -Check for a file: +**Check for a file:** ```csharp -var path = "./path/file_name.txt"; -var file = VirtualFile.GetFile(path); -if(file is not null) { - Console.WriteLine($"File exists, path -> {file.MyPath}"); -} else { - Console.WriteLine($"File not exists") +var filePath = "./path/file_name.txt"; +var file = VirtualFile.GetFile(filePath); + +if (file.Out(out var _file)) +{ + Console.WriteLine($"File exists, path -> {_file.MyPath}"); +} +else +{ + Console.WriteLine("File does not exist"); } ``` -Check for a folder: +**Check for a folder:** ```csharp -var path = "./path/folder_name"; -var folder = VirtualFolder.GetFolder(path); -if(folder is not null) { - Console.WriteLine($"Folder exists, path -> {folder.MyPath}"); -} else { - Console.WriteLine($"Folder not exists") +var folderPath = "./path/folder_name"; +var folder = VirtualFolder.GetFolder(folderPath); + +if (folder.Out(out var _folder)) +{ + Console.WriteLine($"Folder exists, path -> {_folder.MyPath}"); +} +else +{ + Console.WriteLine("Folder does not exist"); } ``` ### 📝 Write and Read Files -Writing to a text file: +**Writing to a text file:** ```csharp -var fileName = "file.txt"; +var fileName = "message.txt"; var file = VirtualIo.RootFolder.GetFileIn(fileName); -if(file is not null) { - var content = "Hello world!"; - await file.WriteStringAsync(content); // Write file - Console.WriteLine($"Writes content [{content}] in file"); - Console.WriteLine($"File content: {await file.ReadStringAsync()}"); //Read file - Console.WriteLine(file.GetInfo()); + +if (file.Out(out var _file)) // Or use GetOrCreate if you want to create it if missing +{ + // Or use GetOrCreate if the file might not exist: + // var file = VirtualFile.GetOrCreate(fileName).Content.Value; + + var content = "Hello world!"; + await _file.WriteStringAsync(content); // Write file + + Console.WriteLine($"Writes content [{content}] in file"); + + var readContent = await _file.ReadStringAsync(); // Read file + Console.WriteLine($"File content: {readContent}"); + + Console.WriteLine($"File size: {_file.SizeInBytes} bytes"); + Console.WriteLine($"Last modified: {_file.LastWriteTime}"); } ``` -Writing with serialization: +**Writing with serialization:** ```csharp -var fileName = "file.txt"; +var fileName = "user.json"; var file = VirtualIo.RootFolder.GetFileIn(fileName); -if(file is not null) { - var content = new ExampleObject("Name", "Lastname", [12, 53, 47]); - file.WriteTContent(content, new JsonSystemSerializer()); // Write and serialize file - Console.WriteLine($"Writes content [\n{content}\n] in file"); - Console.WriteLine($"File content: {file.ReadTContent(new JsonSystemSerializer())}"); //Read and deserialize file - Console.WriteLine(file.GetInfo()); + +if (file.Out(out var _file)) +{ + var content = new { name = "Alice", age = 30 }; // Anonymous type or custom class + await _file.WriteTContentAsync(content, new JsonSystemSerializer()); // Write and serialize + + Console.WriteLine($"Writes content [\n{content}\n] in file"); + + var readUser = await _file.ReadTContentAsync>(new JsonSystemSerializer()); + // Note: Use specific type for ReadTContent, e.g., MyUserClass + // var readUser = await _file.ReadTContentAsync(new JsonSystemSerializer()); + + Console.WriteLine($"Read and deserialized content: {readUser}"); } ``` +**Alternative using `GetOrCreate` (Creates file if missing):** + +```csharp +// Creates file if it doesn't exist, then writes content +var file = VirtualIo.RootFolder.GetOrCreateFileIn("data/data.bin").Content.Value; +file.WriteBytes(new byte[] { 1, 2, 3 }); +``` + ### 🗑️ Delete File and Folder -Deleting a file: +**Deleting a file:** ```csharp -var fileName = "file.txt"; +var fileName = "temp.txt"; var file = VirtualIo.RootFolder.GetFileIn(fileName); -if(file is not null) { - file.DeleteMe(); // Delete file - Console.WriteLine($"File deleted, exists: {file.Exists}"); + +if (file.Out(out var _file)) +{ + _file.DeleteMe(); // Delete file + Console.WriteLine($"File deleted, exists: {_file.Exists}"); // false } ``` -Deleting a folder: +**Deleting a folder (with recursive option):** ```csharp -var folderName = "folder"; +var folderName = "old_data"; var folder = VirtualIo.RootFolder.GetFolderIn(folderName); -if(folder is not null) { - folder.DeleteMe(); //Delete folder - Console.WriteLine($"Folder deleted, exists: {folder.Exists}"); + +if (folder.Out(out var _folder)) +{ + _folder.DeleteMe(full: true); // full = true deletes recursively + Console.WriteLine($"Folder deleted, exists: {_folder.Exists}"); +} +``` + +### 📊 Getting File/Folder Information + +You can easily access metadata properties without writing code: + +```csharp +var file = VirtualIo.RootFolder.GetFileIn("test.txt"); +if (file.Out(out var _file)) +{ + Console.WriteLine($"Name: {_file.Name}"); + Console.WriteLine($"Extension: {_file.Extension}"); + Console.WriteLine($"Creation Time: {_file.CreationTime}"); + Console.WriteLine($"Size in Bytes: {_file.SizeInBytes}"); +} + +// For folders +var folder = VirtualIo.RootFolder.GetFolder("my_project"); +if (folder.Out(out var _folder)) +{ + Console.WriteLine($"Folder Name: {_folder.Name}"); + Console.WriteLine($"Total Files Count: {_folder.GetFilesCount()}"); + Console.WriteLine($"Total Folders Count: {_folder.GetDirectoriesCount()}"); +} +``` + +### 🚀 Enumerating Contents + +```csharp +var rootFiles = VirtualIo.RootFolder.EnumerateFiles(SearchOption.AllDirectories); +foreach (var vFile in rootFiles) +{ + Console.WriteLine($"Found file: {vFile.MyPath}"); +} + +var subFolders = VirtualIo.RootFolder.EnumerateFolders(SearchOption.AllDirectories); +foreach (var vFolder in subFolders) +{ + Console.WriteLine($"Found folder: {vFolder.MyPath}"); } ``` ## 🗃️ Devlog -### 1.2.2 +### 1.3.0 -- Added: field 'RestorePackagesWithLockFile' in 'PropertyGroup' -> *.csproj -- Added: PupaLib.Core -- Changes: New check system through Option used PupaLib.Core -- Rewrite: Usage in readme +* **Added:** `RestorePackagesWithLockFile` field in `PropertyGroup` of `.csproj` files. +* **Added:** `PupaLib.Core` dependency. +* **Changes:** Introduced a new error handling system based on `Option` (from `PupaLib.Core`) across the entire + library. +* **Rewrite:** Major refactoring to ensure `VirtualFolder` and `VirtualFile` methods return `Option` results instead + of `null` or throwing raw exceptions for missing files/folders. ### 1.2.1 diff --git a/src/PupaLib.FileIO.Example/src/ExampleApp.cs b/src/PupaLib.FileIO.Example/src/ExampleApp.cs index ee1af64..e715c4a 100644 --- a/src/PupaLib.FileIO.Example/src/ExampleApp.cs +++ b/src/PupaLib.FileIO.Example/src/ExampleApp.cs @@ -30,7 +30,7 @@ public static async Task Main() { cts.CancelAfter(TimeSpan.FromSeconds(10)); Console.WriteLine("Hello PupaLib.FileIO.Example!"); Example1(); - await Example2(cts.Token); + await Example2(); await Example3_1(cts.Token); await Example3_2(cts.Token); Example4(); @@ -41,7 +41,7 @@ public static async Task Main() { /// /// Creates a file if it doesn't exist and shows file info. /// - public static void Example1(CancellationToken cancellationToken = default) { + public static void Example1() { var fileOption = VirtualIo.RootFolder.GetOrCreateFileIn(ExampleFileName); if (!fileOption.Out(out var file)) return; @@ -51,7 +51,7 @@ public static void Example1(CancellationToken cancellationToken = default) { /// /// Loads existing file or shows null if not found. /// - public static async Task Example2(CancellationToken cancellationToken = default) { + public static async Task Example2() { var fileOption = VirtualIo.RootFolder.GetFileIn(ExampleFileName); if (fileOption.Out(out var file)) Console.Write($"{file.GetInfo()}\n"); From cfd80944809c253a232a49b84c6fc25cac70d623 Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 19:52:57 +0300 Subject: [PATCH 8/9] add: ISSUE_TEMPLATE, SECURITY.md, FUNDING.yml, changes: test.yml & test_and_nuget.yml refresh configuration --- .github/FUNDING.yml | 4 ++ .github/ISSUE_TEMPLATE/bug-report.yml | 75 ++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.yml | 53 ++++++++++++++ .github/workflows/test.yml | 39 ++++++----- .github/workflows/test_and_nuget.yml | 81 ++++++++++++---------- SECURITY.md | 15 ++++ 6 files changed, 216 insertions(+), 51 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml create mode 100644 SECURITY.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..796dc2b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# .github/FUNDING.yml + +custom: + - https://www.donationalerts.com/r/artpupser \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..cbd672b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,75 @@ +name: Bug Report +description: Report a bug to help us improve +title: "[BUG]: " +labels: ["bug"] +assignees: [] + +body: + - type: markdown + attributes: + value: | + Please fill out the form below to help us reproduce and fix the issue. + + - type: input + id: summary + attributes: + label: Short summary + placeholder: Briefly describe the issue + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: How can we reproduce this bug? + placeholder: | + 1. Go to... + 2. Click on... + 3. Observe... + 4. See error... + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + placeholder: What did you expect to happen? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behavior + placeholder: What actually happened? + validations: + required: true + + - type: textarea + id: environment + attributes: + label: Environment + placeholder: | + OS: Windows / Linux / macOS + Browser: Chrome / Firefox / etc + validations: + required: false + + - type: dropdown + id: severity + attributes: + label: Severity + options: + - Low (minor issue) + - Medium (affects functionality) + - High (critical / blocking) + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional context + placeholder: Add any other context, logs, or screenshots \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..6b69a69 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,53 @@ +name: Feature Request +description: Suggest a new feature or improvement +title: "[FEATURE]: " +labels: ["enhancement"] +assignees: [] + +body: + - type: markdown + attributes: + value: | + Thanks for suggesting a feature! Please describe it clearly. + + - type: textarea + id: problem + attributes: + label: Problem + description: What problem are you trying to solve? + placeholder: Describe the issue or limitation + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed solution + description: What would you like to see happen? + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Have you considered other solutions? + validations: + required: false + + - type: dropdown + id: priority + attributes: + label: Priority + options: + - Low + - Medium + - High + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional context + placeholder: Mockups, screenshots, examples, etc. \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 656d8ac..636dd24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,12 +1,13 @@ -name: Testing +name: XUnit testing on: push: - branches: + branches: - "main" + - "dev" + pull_requests: env: - PROJECT_LIB_PATH: 'src/PupaLib.FileIO/PupaLib.FileIO.csproj' PROJECT_TESTS_PATH: 'src/PupaLib.FileIO.Tests/PupaLib.FileIO.Tests.csproj' jobs: @@ -15,24 +16,30 @@ jobs: runs-on: "ubuntu-latest" steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v6 - - name: "Setup dotnet" - uses: actions/setup-dotnet@v3 + - name: "Setup .NET 10" + uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.x' - - name: "Resotore lib project" - run: dotnet restore ${{ env.PROJECT_LIB_PATH }} - - - name: "Resotore test project" - run: dotnet restore ${{ env.PROJECT_TESTS_PATH }} + - name: "Cache Nuget" + uses: actions/cache@v5 + with: + path: ~/.nuget/pacakges + key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }} - - name: "Build lib project" - run: dotnet build ${{ env.PROJECT_LIB_PATH }} + - name: "Resotre all projects in solution" + run: dotnet restore - - name: "Build test project" - run: dotnet build ${{ env.PROJECT_TESTS_PATH }} + - name: "Build all projects in solution" + run: dotnet build --no-restore --configuration Release - name: "Run test" - run: dotnet test ${{ env.PROJECT_TESTS_PATH }} --no-build --verbosity normal \ No newline at end of file + run: dotnet test ${{ env.PROJECT_TESTS_PATH }} --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-report.trx" + + - name: "Upload test report" + uses: actions/upload-artifact@v7 + with: + name: test-report + path: '**/*.trx' \ No newline at end of file diff --git a/.github/workflows/test_and_nuget.yml b/.github/workflows/test_and_nuget.yml index 0293fa1..9d3ceed 100644 --- a/.github/workflows/test_and_nuget.yml +++ b/.github/workflows/test_and_nuget.yml @@ -1,4 +1,4 @@ -name: Deploy to nuget +name: XUnit test & Deploy nuget package on: push: @@ -19,58 +19,69 @@ jobs: runs-on: "ubuntu-latest" steps: - name: 'Checkout' - uses: actions/checkout@v3 - - - name: "Setup dotnet" - uses: actions/setup-dotnet@v3 + uses: actions/checkout@v6 + + - name: "Setup .NET 10" + uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.x' - - - name: "Resotore lib project" - run: dotnet restore ${{ env.PROJECT_LIB_PATH }} - - - name: "Resotore test project" - run: dotnet restore ${{ env.PROJECT_TESTS_PATH }} - - - name: "Build lib project" - run: dotnet build ${{ env.PROJECT_LIB_PATH }} - - - name: "Build test project" - run: dotnet build ${{ env.PROJECT_TESTS_PATH }} - + + - name: "Cache Nuget" + uses: actions/cache@v5 + with: + path: ~/.nuget/pacakges + key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }} + + - name: "Resotre all projects in solution" + run: dotnet restore + + - name: "Build all projects in solution" + run: dotnet build --no-restore --configuration Release + - name: "Run test" - run: dotnet test ${{ env.PROJECT_TESTS_PATH }} --no-build --verbosity normal + run: dotnet test ${{ env.PROJECT_TESTS_PATH }} --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-report.trx" + + - name: "Upload test report" + uses: actions/upload-artifact@v7 + with: + name: test-report + path: '**/*.trx' deploy: name: "Deploy" runs-on: 'ubuntu-latest' needs: test steps: - name: 'Checkout' - uses: actions/checkout@v3 - - - name: "Setup dotnet" - uses: actions/setup-dotnet@v3 + uses: actions/checkout@v6 + + - name: "Setup .NET 10" + uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.x' - + + - name: "Cache Nuget" + uses: actions/cache@v5 + with: + path: ~/.nuget/pacakges + key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }} + - name: "Restore package" run: dotnet restore ${{ env.PROJECT_LIB_PATH }} - + - name: "Build package" - run: dotnet build ${{ env.PROJECT_LIB_PATH }} --no-restore -c Release - - - name: "Get version" - id: version - uses: battila7/get-version-action@v2 - + run: dotnet build ${{ env.PROJECT_LIB_PATH }} --no-restore --configuration Release + + - name: Extract version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: "Pack pacakge" - run: dotnet pack ${{ env.PROJECT_LIB_PATH }} --no-restore --no-build -c Release --include-symbols -p:PackageVersion=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIR }} - + run: dotnet pack ${{ env.PROJECT_LIB_PATH }} --no-restore --no-build -c Release --include-symbols -p:PackageVersion=${{ env.VERSION }} --output ${{ env.PACKAGE_OUTPUT_DIR }} + - name: "Push package NUGET" run: dotnet nuget push --skip-duplicate ${{ env.OUTPUT_NUPKG_FILE }} -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URI }} - + - name: "Add source for github" run: dotnet nuget add source --username ${{ env.GITHUB_USERNAME }} --password ${{ secrets.GIT_AUTH_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/${{ env.GITHUB_USERNAME }}/index.json" - + - name: "Push package GITHUB" run: dotnet nuget push --skip-duplicate ${{ env.OUTPUT_NUPKG_FILE }} -k ${{ secrets.GIT_AUTH_TOKEN }} -s "github" \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..adb920b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +## Supported Versions + +Only the latest version of this project is supported with security updates. + +| Version | Supported | +| ------- | --------- | +| latest | ✔ | + +## Reporting a Vulnerability + +If you discover a security vulnerability, please report it privately via GitHub Security Advisories. + +Please do NOT open a public issue. + +We aim to respond within 48–72 hours. \ No newline at end of file From 8d7d2fb73fd11654c78335ef763bfdb6346faa6a Mon Sep 17 00:00:00 2001 From: Artpupser Date: Fri, 24 Apr 2026 20:10:52 +0300 Subject: [PATCH 9/9] fix: workflows/**/*.yml --- .github/workflows/test.yml | 3 +++ .github/workflows/test_and_nuget.yml | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 636dd24..3402c8a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,9 @@ on: env: PROJECT_TESTS_PATH: 'src/PupaLib.FileIO.Tests/PupaLib.FileIO.Tests.csproj' +permissions: + contents: read + jobs: test: name: "Test" diff --git a/.github/workflows/test_and_nuget.yml b/.github/workflows/test_and_nuget.yml index 9d3ceed..a7b06f7 100644 --- a/.github/workflows/test_and_nuget.yml +++ b/.github/workflows/test_and_nuget.yml @@ -13,6 +13,9 @@ env: NUGET_SOURCE_URI: "https://api.nuget.org/v3/index.json" GITHUB_USERNAME: "Artpupser" +permissions: + contents: read + jobs: test: name: "Test" @@ -81,7 +84,7 @@ jobs: run: dotnet nuget push --skip-duplicate ${{ env.OUTPUT_NUPKG_FILE }} -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URI }} - name: "Add source for github" - run: dotnet nuget add source --username ${{ env.GITHUB_USERNAME }} --password ${{ secrets.GIT_AUTH_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/${{ env.GITHUB_USERNAME }}/index.json" + run: dotnet nuget add source --username ${{ env.GITHUB_USERNAME }} --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/${{ env.GITHUB_USERNAME }}/index.json" - name: "Push package GITHUB" - run: dotnet nuget push --skip-duplicate ${{ env.OUTPUT_NUPKG_FILE }} -k ${{ secrets.GIT_AUTH_TOKEN }} -s "github" \ No newline at end of file + run: dotnet nuget push --skip-duplicate ${{ env.OUTPUT_NUPKG_FILE }} -k ${{ secrets.GITHUB_TOKEN }} -s "github" \ No newline at end of file