{"id":20632333,"url":"https://github.com/codybartfast/ccfile","last_synced_at":"2025-09-13T12:39:44.348Z","repository":{"id":41280673,"uuid":"501928736","full_name":"codybartfast/ccfile","owner":"codybartfast","description":"Source for the NuGet package: www.nuget.org/packages/Fmbm.CCFile/","archived":false,"fork":false,"pushed_at":"2022-08-08T16:54:51.000Z","size":91,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-10T16:07:07.014Z","etag":null,"topics":["file-backup","file-read","file-write"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codybartfast.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-10T06:38:06.000Z","updated_at":"2022-08-08T15:14:59.000Z","dependencies_parsed_at":"2022-08-10T01:43:30.820Z","dependency_job_id":null,"html_url":"https://github.com/codybartfast/ccfile","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/codybartfast/ccfile","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codybartfast%2Fccfile","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codybartfast%2Fccfile/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codybartfast%2Fccfile/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codybartfast%2Fccfile/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codybartfast","download_url":"https://codeload.github.com/codybartfast/ccfile/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codybartfast%2Fccfile/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274961775,"owners_count":25381893,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-13T02:00:10.085Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["file-backup","file-read","file-write"],"created_at":"2024-11-16T14:15:49.965Z","updated_at":"2025-09-13T12:39:44.305Z","avatar_url":"https://github.com/codybartfast.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"CCFile\n======\n\nRead, write and serialize to file - simply and robustly.\n\nFeatures:\n\n* Thread-safe reads and writes (similar to `File.ReadAllText`) plus\n  serialization.\n* Convenient, thread-safe `ReadOrWrite` method for initialization (similar\n  to [`ConcurrentDictionary.GetOrAdd`][MSGetOrAdd]).\n* Thread-safe `Modify` method.\n* Automatically creates a backup of existing files.\n* Supports additional archiving or versioning of files.\n* Reduces likelihood of partial writes and detects them if they do happen.\n\n__Warning:__ _Although intended to be robust, this is a first release and so may\nnot be as reliable as hoped. It may have dreadful bugs._\n\n\u0026nbsp;\n\nFor Me, By Me (FMBM)\n--------------------\n\nThis was created primarily for use by the author.  It has only been tested\nin limited environments.  It is intended for getting ad-hoc  applications up\nand running quickly.  It probably is not suitable for complex, production,\nnor evolving projects.  (The name is inspired by the [Fubu][Fubu],\n _For Us, By Us_, project, but there is no other connection.)\n\n\u0026nbsp;\n\nContents\n--------\n\n* [CCFile Basic Usage](#ccfile-basic-usage)  \n* [CCValue Basic Usage](#ccvalue-basic-usage)  \n* [Symbolic Links](#symbolic-links)  \n* [ReadOrWrite](#readorwrite)  \n* [Modify](#modify)  \n* [Exists and Delete](#exists-and-delete)  \n* [Archive](#archive)  \n* [Interfaces](#interfaces)  \n* [Files Check](#files-check)  \n* [Name](#why-is-it-called-ccfile)  \n\n\u0026nbsp;\n\nCCFile Basic Usage\n------------------\n\n`CCFile` supports `Read`, `Write`, `Modify`, and `ReadOrWrite` for each of\n`bytes[]`, `string` and 'values'.  \n\nThis shows a 'value' being written to disk and then read back as a `string`,\nas a `byte[]`, and as a 'value':\n\n```C#\nusing Fmbm.IO;\n\n// Create new CCFile\nvar ccfile = new CCFile(\"CCFile_Sample.txt\");\n\n// Serialize a list to disk\nccfile.WriteValue(new List\u003cstring\u003e { \"Apple\", \"Banana\", \"Cherry\" });\n\n// Read file contents as text\nConsole.WriteLine(ccfile.ReadText());\n\n// Read file contents as bytes\nConsole.WriteLine(ccfile.ReadBytes().Length);\n\n// Read file contents as a value\nConsole.WriteLine(ccfile.ReadValue\u003cList\u003cstring\u003e\u003e().Last());\n\n// OUTPUT:\n// [\n//   \"Apple\",\n//   \"Banana\",\n//   \"Cherry\"\n// ]\n// 27\n// Cherry\n```\n\n\u0026nbsp;\n\nCCValue Basic Usage\n-------------------\n\n`CCValue` is a strongly typed version that supports `Read`, `Write`,\n`Modify`, and `ReadOrWrite` but only for 'values'.\n\nThis shows a value being written to disk and then read back as a 'value':\n\n```C#\nusing Fmbm.IO;\n\nvar ccvalue = new CCValue\u003cList\u003cstring\u003e\u003e(\"CCFile_Sample.txt\");\n\n// Serialize a list to disk\nccvalue.Write(new List\u003cstring\u003e { \"Apple\", \"Banana\", \"Cherry\" });\n\n// Deserialize file and get last element of list\nConsole.WriteLine(ccvalue.Read().Last());\n\n// OUTPUT:\n// Cherry\n\n```\n\n\u0026nbsp;\n\nSymbolic Links\n--------------\n\nThe file's FullName is used as the key for synchronizing access it.  This should\nwork correctly across different instances of `CCFile` that access the same file\neven if they are created with different relative paths or with different casing.\nHowever if a file is accessible though a symbolic link then synchronization may\nnot work as expected if different instances of `CCFile` use the symbolic\nlink inconsistently.  E.g. if `CCFile`s are instantiated with\n`/apple/banana/cherry/thefile.txt` and `/apple/sldir/thefile.txt` then access\nto `thefile.txt` will not be thread-safe.\n\n\u0026nbsp;\n\nReadOrWrite\n-----------\n\n`ReadOrWrite` is a thread-safe and convenient way to set the initial content\nof the file if the file does not already exist.\n\nIf the file does not already exist then the `getInitialValue` argument is\ncalled and the result is written to the file.  If the file already exists\nthen `ReadOrWrite` will return the existing file content.  \n\n```C#\nusing Fmbm.IO;\n\nvar ccfile = new CCFile(\"CCFile_Sample.txt\");\n\n// If file does not already exist 'getInitialValue' will be called:\nvar result1 = ccfile.ReadOrWriteText(() =\u003e \"Apple\");\nConsole.WriteLine(result1);\n\n// 'getInitialValue' not called because file now exists.\nvar result2 = ccfile.ReadOrWriteText(() =\u003e \"Banana\");\nConsole.WriteLine(result2);\n\n// OUTPUT:\n// Apple\n// Apple\n```\n\n\u0026nbsp;\n\nModify\n------\n\n`Modify` provides a thread-safe way to change the contents of the file.\n\n```C#\nusing Fmbm.IO;\n\nvar ccvalue = new CCValue\u003cstring[]\u003e(\"CCFile_Sample.txt\");\n\nccvalue.Write(new[] { \"Cherry\", \"Banana\", \"Apple\" });\n\nConsole.WriteLine(String.Join(\", \", ccvalue.Read()));\n\nccvalue.Modify(fruit =\u003e\n{\n    Array.Sort(fruit);\n    return fruit;\n});\n\nConsole.WriteLine(String.Join(\", \", ccvalue.Read()));\n\n// OUTPUT:\n// Cherry, Banana, Apple\n// Apple, Banana, Cherry\n```\n\n\u0026nbsp;\n\nExists and Delete\n-----------------\n\n`Exists` indicates whether the file exists.\n\n`Delete` deletes the file and any backup or temporary file.\n\n```C#\nusing Fmbm.IO;\n\nvar ccfile = new CCFile(\"CCFile_Sample.txt\");\nConsole.WriteLine(ccfile.Exists);\n\nccfile.WriteText(\"\");\nConsole.WriteLine(ccfile.Exists);\n\nccfile.Delete();\nConsole.WriteLine(ccfile.Exists);\n\n// OUTPUT:\n// False\n// True\n// False\n\n```\n\n\u0026nbsp;\n\nArchive\n-------\n\nThe constructors of `CCFile` and `CCValue` take an optional `archive`\nparameter.  `archive` is an Action that takes a string and a nullable\nstring.\n\n`archive` is called when any write completes (including ReadOrWrite and\nModify).  The first argument is the path to the new or updated file.  The\nsecond argument is the path to the backup file, or `null` if there is no\nbackup file.\n\n```C#\nusing Fmbm.IO;\n\nvoid Archive(string filePath, string? backPath)\n{\n    var after = File.ReadAllText(filePath);\n    var before = backPath is null ? \"\u003cnone\u003e\" : File.ReadAllText(backPath);\n\n    Console.Write($@\"\n** File Updated **\n==================\nBefore: {before}\nAfter: {after}\n\");\n}\n\nvar ccvalue =\n    new CCValue\u003cDictionary\u003cstring, string\u003e\u003e(\"CCFile_Sample.txt\", Archive);\n\nccvalue.ReadOrWrite(() =\u003e\n    new Dictionary\u003cstring, string\u003e { { \"A\", \"Apple\" }, { \"B\", \"Banana\" } });\n\nccvalue.Modify(fruit =\u003e\n{\n    fruit.Add(\"C\", \"Cherry\");\n    return fruit;\n});\n\n// OUTPUT:\n\n// ** File Updated **\n// ==================\n// Before: \u003cnone\u003e\n// After: {\n//   \"A\": \"Apple\",\n//   \"B\": \"Banana\"\n// }\n\n// ** File Updated **\n// ==================\n// Before: {\n//   \"A\": \"Apple\",\n//   \"B\": \"Banana\"\n// }\n// After: {\n//   \"A\": \"Apple\",\n//   \"B\": \"Banana\",\n//   \"C\": \"Cherry\"\n// }\n```\n\n\u0026nbsp;\n\nInterfaces\n----------\n\n`CCFile` implements the interface `ICCFile` which extends: `ICCBinary`,\n`ICCText` and `ICCGeneric`\n\n`CCValue` implements the interface `ICCValue`\n\n`ICCBinary`, `ICCText`, `ICCGeneric` and `ICCValue` each defines\n`Exists` and defines their own versions of:\n\n* Read\n* Write\n* ReadOrWrite\n* Modify\n\n\u0026nbsp;\n\nFiles Check\n-----------\n\nThe normal write process is:\n\n* New data is written to the _temporary_ path.\n* The existing file is moved to the _backup_ path overwriting any existing\n  backup.\n* The _temporary_ file is moved to the _file_ path.\n* Archive is called.\n\nThe _temporary_ path is the _file_ path with a `.tmp` extension appended.\n\nThe _backup_ path is the _file_ path with a `.bak` extension appended.\n\nBefore reads and writes `CCFile` checks to determine if a previous write\nmight be incomplete.  If the check fails then a `CCFileException` is thrown.\n\nThis table shows the write steps and shows the combinations of files that\nwould fail the check for an incomplete write and cause a `CCFileException`\nto be thrown.\n\n```Text\nLower case f, b: existing file.\nUpper case T, F, B: updated file.\n┌──────┬──────┬──────┬─────────────────────────────────────────────────────┐\n│ .tmp │ file │ .bak │                                                     │\n├──────┴──────┴──────┴─────────────────────────────────────────────────────┤\n│                                                                          │\n│                              First write                                 │\n├──────┬──────┬──────┬─────────────────────────────────────────────────────┤\n│      │      │      │ Pass: initial state.                                │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│  T   │      │      │ Fail: .tmp may or may no be complete.               │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│      │  F   │      │ Pass: expected state after first write.             │\n├──────┴──────┴──────┴─────────────────────────────────────────────────────┤\n│                                                                          │\n│                              Second write                                │\n├──────┬──────┬──────┬─────────────────────────────────────────────────────┤\n│      │  f   │      │ Pass: initial state.                                │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│  T   │  f   │      │ Fail: .tmp may or may not be complete.              │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│  T   │      │  B   │ Fail: but both files should be complete.            │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│      │  F   │  B   │ Pass: expected state after second write.            │\n├──────┴──────┴──────┴─────────────────────────────────────────────────────┤\n│                                                                          │\n│                              Subsequent writes:                          │\n├──────┬──────┬──────┬─────────────────────────────────────────────────────┤\n│      │  f   │  b   │ Pass: initial state.                                │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│  T   │  f   │  b   │ Fail: .tmp may or may not be complete.              │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│  T   │      │  B   │ Fail: but both files should be complete.            │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│      │  F   │  B   │ Pass: expected state after subsequent writes.       │\n├──────┴──────┴──────┴─────────────────────────────────────────────────────┤\n│                                                                          │\n│            If archive function moves or deletes the .bck file:           │\n├──────┬──────┬──────┬─────────────────────────────────────────────────────┤\n│      │  f   │      │ Pass: initial state.                                │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│  T   │  f   │      │ Fail: .tmp may or may not be complete.              │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│  T   │      │  B   │ Fail: but both files should be complete.            │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│      │  F   │  B   │ Pass: before archive is called.                     │\n├──────┼──────┼──────┼─────────────────────────────────────────────────────┤\n│      │  F   │      │ Pass: normal after archive moves/deletes .bak.      │\n├──────┴──────┴──────┼─────────────────────────────────────────────────────┤\n│                    │                                                     │\n│ Other combinations │                  ¯\\_(ツ)_/¯                         │\n└────────────────────┴─────────────────────────────────────────────────────┘\n```\n\nWhy is it called 'CCFile'?\n--------------------------\n\nBecause it's a carbon copying, conveniently converting, concurrency\nconscious, crash catching file wrapper.\n\n[Fubu]: \u003chttps://fubumvc.github.io/\u003e\n[MSGetOrAdd]: \u003chttps://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.getoradd?view=net-6.0#system-collections-concurrent-concurrentdictionary-2-getoradd(-0-system-func((-0-1)))\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodybartfast%2Fccfile","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodybartfast%2Fccfile","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodybartfast%2Fccfile/lists"}