https://github.com/codybartfast/ccfile
Source for the NuGet package: www.nuget.org/packages/Fmbm.CCFile/
https://github.com/codybartfast/ccfile
file-backup file-read file-write
Last synced: 9 months ago
JSON representation
Source for the NuGet package: www.nuget.org/packages/Fmbm.CCFile/
- Host: GitHub
- URL: https://github.com/codybartfast/ccfile
- Owner: codybartfast
- License: mit
- Created: 2022-06-10T06:38:06.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2022-08-08T16:54:51.000Z (almost 4 years ago)
- Last Synced: 2025-09-10T16:07:07.014Z (9 months ago)
- Topics: file-backup, file-read, file-write
- Language: C#
- Homepage:
- Size: 88.9 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: ReadMe.md
- License: license.txt
Awesome Lists containing this project
README
CCFile
======
Read, write and serialize to file - simply and robustly.
Features:
* Thread-safe reads and writes (similar to `File.ReadAllText`) plus
serialization.
* Convenient, thread-safe `ReadOrWrite` method for initialization (similar
to [`ConcurrentDictionary.GetOrAdd`][MSGetOrAdd]).
* Thread-safe `Modify` method.
* Automatically creates a backup of existing files.
* Supports additional archiving or versioning of files.
* Reduces likelihood of partial writes and detects them if they do happen.
__Warning:__ _Although intended to be robust, this is a first release and so may
not be as reliable as hoped. It may have dreadful bugs._
For Me, By Me (FMBM)
--------------------
This was created primarily for use by the author. It has only been tested
in limited environments. It is intended for getting ad-hoc applications up
and running quickly. It probably is not suitable for complex, production,
nor evolving projects. (The name is inspired by the [Fubu][Fubu],
_For Us, By Us_, project, but there is no other connection.)
Contents
--------
* [CCFile Basic Usage](#ccfile-basic-usage)
* [CCValue Basic Usage](#ccvalue-basic-usage)
* [Symbolic Links](#symbolic-links)
* [ReadOrWrite](#readorwrite)
* [Modify](#modify)
* [Exists and Delete](#exists-and-delete)
* [Archive](#archive)
* [Interfaces](#interfaces)
* [Files Check](#files-check)
* [Name](#why-is-it-called-ccfile)
CCFile Basic Usage
------------------
`CCFile` supports `Read`, `Write`, `Modify`, and `ReadOrWrite` for each of
`bytes[]`, `string` and 'values'.
This shows a 'value' being written to disk and then read back as a `string`,
as a `byte[]`, and as a 'value':
```C#
using Fmbm.IO;
// Create new CCFile
var ccfile = new CCFile("CCFile_Sample.txt");
// Serialize a list to disk
ccfile.WriteValue(new List { "Apple", "Banana", "Cherry" });
// Read file contents as text
Console.WriteLine(ccfile.ReadText());
// Read file contents as bytes
Console.WriteLine(ccfile.ReadBytes().Length);
// Read file contents as a value
Console.WriteLine(ccfile.ReadValue>().Last());
// OUTPUT:
// [
// "Apple",
// "Banana",
// "Cherry"
// ]
// 27
// Cherry
```
CCValue Basic Usage
-------------------
`CCValue` is a strongly typed version that supports `Read`, `Write`,
`Modify`, and `ReadOrWrite` but only for 'values'.
This shows a value being written to disk and then read back as a 'value':
```C#
using Fmbm.IO;
var ccvalue = new CCValue>("CCFile_Sample.txt");
// Serialize a list to disk
ccvalue.Write(new List { "Apple", "Banana", "Cherry" });
// Deserialize file and get last element of list
Console.WriteLine(ccvalue.Read().Last());
// OUTPUT:
// Cherry
```
Symbolic Links
--------------
The file's FullName is used as the key for synchronizing access it. This should
work correctly across different instances of `CCFile` that access the same file
even if they are created with different relative paths or with different casing.
However if a file is accessible though a symbolic link then synchronization may
not work as expected if different instances of `CCFile` use the symbolic
link inconsistently. E.g. if `CCFile`s are instantiated with
`/apple/banana/cherry/thefile.txt` and `/apple/sldir/thefile.txt` then access
to `thefile.txt` will not be thread-safe.
ReadOrWrite
-----------
`ReadOrWrite` is a thread-safe and convenient way to set the initial content
of the file if the file does not already exist.
If the file does not already exist then the `getInitialValue` argument is
called and the result is written to the file. If the file already exists
then `ReadOrWrite` will return the existing file content.
```C#
using Fmbm.IO;
var ccfile = new CCFile("CCFile_Sample.txt");
// If file does not already exist 'getInitialValue' will be called:
var result1 = ccfile.ReadOrWriteText(() => "Apple");
Console.WriteLine(result1);
// 'getInitialValue' not called because file now exists.
var result2 = ccfile.ReadOrWriteText(() => "Banana");
Console.WriteLine(result2);
// OUTPUT:
// Apple
// Apple
```
Modify
------
`Modify` provides a thread-safe way to change the contents of the file.
```C#
using Fmbm.IO;
var ccvalue = new CCValue("CCFile_Sample.txt");
ccvalue.Write(new[] { "Cherry", "Banana", "Apple" });
Console.WriteLine(String.Join(", ", ccvalue.Read()));
ccvalue.Modify(fruit =>
{
Array.Sort(fruit);
return fruit;
});
Console.WriteLine(String.Join(", ", ccvalue.Read()));
// OUTPUT:
// Cherry, Banana, Apple
// Apple, Banana, Cherry
```
Exists and Delete
-----------------
`Exists` indicates whether the file exists.
`Delete` deletes the file and any backup or temporary file.
```C#
using Fmbm.IO;
var ccfile = new CCFile("CCFile_Sample.txt");
Console.WriteLine(ccfile.Exists);
ccfile.WriteText("");
Console.WriteLine(ccfile.Exists);
ccfile.Delete();
Console.WriteLine(ccfile.Exists);
// OUTPUT:
// False
// True
// False
```
Archive
-------
The constructors of `CCFile` and `CCValue` take an optional `archive`
parameter. `archive` is an Action that takes a string and a nullable
string.
`archive` is called when any write completes (including ReadOrWrite and
Modify). The first argument is the path to the new or updated file. The
second argument is the path to the backup file, or `null` if there is no
backup file.
```C#
using Fmbm.IO;
void Archive(string filePath, string? backPath)
{
var after = File.ReadAllText(filePath);
var before = backPath is null ? "" : File.ReadAllText(backPath);
Console.Write($@"
** File Updated **
==================
Before: {before}
After: {after}
");
}
var ccvalue =
new CCValue>("CCFile_Sample.txt", Archive);
ccvalue.ReadOrWrite(() =>
new Dictionary { { "A", "Apple" }, { "B", "Banana" } });
ccvalue.Modify(fruit =>
{
fruit.Add("C", "Cherry");
return fruit;
});
// OUTPUT:
// ** File Updated **
// ==================
// Before:
// After: {
// "A": "Apple",
// "B": "Banana"
// }
// ** File Updated **
// ==================
// Before: {
// "A": "Apple",
// "B": "Banana"
// }
// After: {
// "A": "Apple",
// "B": "Banana",
// "C": "Cherry"
// }
```
Interfaces
----------
`CCFile` implements the interface `ICCFile` which extends: `ICCBinary`,
`ICCText` and `ICCGeneric`
`CCValue` implements the interface `ICCValue`
`ICCBinary`, `ICCText`, `ICCGeneric` and `ICCValue` each defines
`Exists` and defines their own versions of:
* Read
* Write
* ReadOrWrite
* Modify
Files Check
-----------
The normal write process is:
* New data is written to the _temporary_ path.
* The existing file is moved to the _backup_ path overwriting any existing
backup.
* The _temporary_ file is moved to the _file_ path.
* Archive is called.
The _temporary_ path is the _file_ path with a `.tmp` extension appended.
The _backup_ path is the _file_ path with a `.bak` extension appended.
Before reads and writes `CCFile` checks to determine if a previous write
might be incomplete. If the check fails then a `CCFileException` is thrown.
This table shows the write steps and shows the combinations of files that
would fail the check for an incomplete write and cause a `CCFileException`
to be thrown.
```Text
Lower case f, b: existing file.
Upper case T, F, B: updated file.
┌──────┬──────┬──────┬─────────────────────────────────────────────────────┐
│ .tmp │ file │ .bak │ │
├──────┴──────┴──────┴─────────────────────────────────────────────────────┤
│ │
│ First write │
├──────┬──────┬──────┬─────────────────────────────────────────────────────┤
│ │ │ │ Pass: initial state. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ T │ │ │ Fail: .tmp may or may no be complete. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ │ F │ │ Pass: expected state after first write. │
├──────┴──────┴──────┴─────────────────────────────────────────────────────┤
│ │
│ Second write │
├──────┬──────┬──────┬─────────────────────────────────────────────────────┤
│ │ f │ │ Pass: initial state. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ T │ f │ │ Fail: .tmp may or may not be complete. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ T │ │ B │ Fail: but both files should be complete. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ │ F │ B │ Pass: expected state after second write. │
├──────┴──────┴──────┴─────────────────────────────────────────────────────┤
│ │
│ Subsequent writes: │
├──────┬──────┬──────┬─────────────────────────────────────────────────────┤
│ │ f │ b │ Pass: initial state. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ T │ f │ b │ Fail: .tmp may or may not be complete. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ T │ │ B │ Fail: but both files should be complete. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ │ F │ B │ Pass: expected state after subsequent writes. │
├──────┴──────┴──────┴─────────────────────────────────────────────────────┤
│ │
│ If archive function moves or deletes the .bck file: │
├──────┬──────┬──────┬─────────────────────────────────────────────────────┤
│ │ f │ │ Pass: initial state. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ T │ f │ │ Fail: .tmp may or may not be complete. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ T │ │ B │ Fail: but both files should be complete. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ │ F │ B │ Pass: before archive is called. │
├──────┼──────┼──────┼─────────────────────────────────────────────────────┤
│ │ F │ │ Pass: normal after archive moves/deletes .bak. │
├──────┴──────┴──────┼─────────────────────────────────────────────────────┤
│ │ │
│ Other combinations │ ¯\_(ツ)_/¯ │
└────────────────────┴─────────────────────────────────────────────────────┘
```
Why is it called 'CCFile'?
--------------------------
Because it's a carbon copying, conveniently converting, concurrency
conscious, crash catching file wrapper.
[Fubu]:
[MSGetOrAdd]: