Compare commits

...

23 commits
1.0.0 ... main

Author SHA1 Message Date
26d18bb81a Update .forgejo/workflows/test.yml 2024-10-16 15:50:36 +02:00
3d66292a55 Update .forgejo/workflows/test.yml 2024-10-16 15:49:44 +02:00
b2161b3011 Update .forgejo/workflows/test.yml 2024-10-16 15:48:02 +02:00
65eb3c8d95 Update .forgejo/workflows/test.yml 2024-10-16 15:46:38 +02:00
a5cc909153 Merge pull request 'Add .forgejo/workflows/test.yml' (#6) from dev into main
Reviewed-on: #6
2024-10-16 15:44:44 +02:00
beb1c6ca92 Add .forgejo/workflows/test.yml 2024-10-16 15:44:04 +02:00
f6e2991ae1 Merge pull request 'Update to 1.1.0' (#5) from dev into main
Reviewed-on: #5
2024-09-26 17:32:20 +02:00
dae6c8f9dc Update CHANGELOG.md 2024-09-26 17:30:37 +02:00
4d7c342dbd final touches 2024-09-26 17:29:15 +02:00
4076974f6b Bugfix and new contentToString 2024-09-26 17:23:09 +02:00
95b06f1465 Update README.md 2024-09-26 15:07:14 +02:00
f9647915a5 Add migration-guide.md 2024-09-26 15:05:00 +02:00
667d74ddcf Update CHANGELOG.md 2024-09-26 14:57:04 +02:00
41f8c6b41d Add CHANGELOG 2024-09-26 14:47:15 +02:00
5d20dfd967 Update README.md 2024-09-26 14:47:01 +02:00
cbaa413cd0 Update README.md 2024-09-26 14:43:46 +02:00
f4a2fee0d7 Update README.md 2024-09-26 14:34:17 +02:00
6572a85cfc Update README.md 2024-09-26 14:33:57 +02:00
dbc38e0195 Add Bofa.Deserialize 2024-09-26 14:32:37 +02:00
c8629face9 Added strict and required 2024-09-26 14:19:23 +02:00
fab910e2fc Added serialization for hash 2024-09-26 13:20:45 +02:00
edcb459ad1 Added hash type 2024-09-26 12:04:43 +02:00
ac7bb9191d Added rgb and rgba support 2024-09-26 10:38:00 +02:00
16 changed files with 343 additions and 16 deletions

View file

@ -0,0 +1,11 @@
on:
workflow_dispatch:
jobs:
Test Bofa:
steps:
- name: Setup-Beef
id: setup-beef
uses: actions/setup-beef@main

22
CHANGELOG.md Normal file
View file

@ -0,0 +1,22 @@
- Version 1.1.0
- __Types__
- New type: __rgb__
- New type: __rgba__
- New type: __hash__
- Added support for ppkg (https://code.booklordofthe.dev/Booklordofthedings/pkkg)
- More relaxed serialization/deserialization:
- _Fields can now fail to be parsed without returning an error_
- Added __strict__ parameter to __BofaSerialize__:
- _This returns the old fail on parse behaviour_
- Added new Attribute: __BofaRequired__
- _Allows parts of a parse to fail but always ensures that this field could be parsed_
- Added method: __bool Bofa.Deserialize<T>(T, Dictionary<StringView, Bofa>)__
- _Previously users had to wrap .bofa files in a top level object if they wanted to have it mapped via IBofaParseable_
- _Now this method wraps it for them, so that no top level objects are required if the file represents a whole object_
- Added method: __ToStringContents__
- _Used to enable roundtripping with Bofa.Deserialize_
- Version 1.0.0
- Initial Version
- Fast parsing
- Add automated serialization generation

View file

@ -16,11 +16,13 @@ a Array
## Index
- How to install
- Changelog
- The Bofa notation
- Parser notes
- Bofa parser API usage
- Bofa serialization API usage
- Potential improvements
- [Changelog](CHANGELOG.md)
- [Migration](migration-guide.md)
## How to install
- Download the repository, either with git, the download zip functionality or the release tab
@ -34,12 +36,6 @@ a Array
- Add the Bofa namespace to the files you want to use Bofa with
- Use Bofa
## Changelog
- Version 1.0.0
- Initial Version
- Fast parsing
- Add automated serialization generation
## The Bofa notation
The notation itself is pretty simple.
Every line contains one Bofa "Entry", but empty lines and comments are also allowed.
@ -133,15 +129,35 @@ class ExampleClass
}
```
## Bofa serialization API usage
Serialization can be used to map types from and to Bofa with only minimal work required.
The interface IBofaParseable indicates wether a specific type can be parsed.
You can manually add it to your class and its also available on some corelib types by default.
There is also an attribute you can use to make Bofa attempt to generate the interface at compile time.
```cs
[BofaSerialize(Strict = false)] //false by default. If set to true it will fail when it cant find/parse a field
class Example
{
//It attempts to parse all public fields
//[BofaInclue(Name = "FieldName")]
//Can be used to include private fields and to rename fields with whatever name you want
public int anInteger = 1;
public String aString = new .("Value") ~ delete _;
public List<String> listOfStrings = new .() ~ DeleteContainerAndItems!(_); //As long as the T can be parsed it will also parse lists and dictionaries
[BofaRequired] public int anotherOne = 0; //Parsing will fail if this field isnt present, since its marked as required
}
```
`Bofa.Deserialize(Example, Dictionary<StringView, Bofa>)` can be used to directly map the result of a parse to a field.
## Potential improvements
- There is still alot of speed left on the table
- Reduce the amount of allocations
- Multithread initial parsing
- New useful types
- Color
- Version
- Encrypted raw data
- More automatic serialization/deserialization in Corlib
- Hashes
- Pointers
- Tuples

13
migration-guide.md Normal file
View file

@ -0,0 +1,13 @@
## 1.0.0 -> 1.1.0
### Required
- Add (true) to every instance of BofaSerializeable to get the old stricter behaviour for serializing.
- Switches over Bofa without default now need to include rgb, rgba and hash.
### Optional
- If you have used a custom field for any of these, it may be good to change them to native types now.
- If you have previously used files that looked like this for serialization you can now remove the top level object and use `Bofa.Deserialize<T>` instead.
```
o object
l line of text
n value 34354545.6
```

13
pkkg Normal file
View file

@ -0,0 +1,13 @@
#This is the pkkg template file
l Name Bofa
t Desc A simple object notation
l Url https://code.booklordofthe.dev/Booklordofthedings/Bofa
a Authors
o Book
l Name Booklordofthedings
l Url https://Boooklordofthe.dev
a Dependencies

View file

@ -14,6 +14,27 @@ class Bofa
public StringView Typename;
public BofaValue Value;
public static bool Deserialize<T>(ref T pType, Dictionary<StringView, Bofa> pDictionary) where T : Bofa.Serialization.IBofaParseable
{
Bofa b = scope .();
b.Type = .Object;
b.Value.Object = pDictionary;
defer {b.Type = .Integer;}
return pType.Deserialize(b);
}
///Only turns the contents of this object into a string and not itself
public void ToStringContents(String strBuffer)
{
if(this.Type == .Object)
for(var i in this.Value.Object)
i.value.ToString(strBuffer);
else if(this.Type == .Array)
for(var i in this.Value.Array)
i.ToString(strBuffer);
}
public override void ToString(String strBuffer)
{
int64 depth = 0;
@ -91,7 +112,13 @@ class Bofa
}
}
}
case .RGB:
strBuffer.Append(scope $"rgb {Name} {Value.Rgb[0]:X2} {Value.Rgb[1]:X2} {Value.Rgb[2]:X2}");
case .RGBA:
strBuffer.Append(scope $"rgb {Name} {Value.Rgba[0]:X2} {Value.Rgba[1]:X2} {Value.Rgba[2]:X2} {Value.Rgba[3]:X2}");
case .Hash:
strBuffer.Append(scope $"hash {Name} sha256-{Value.Hash}");
}
}
///Deletes this object without filling the stack

View file

@ -51,7 +51,11 @@ class BofaParser
if (entry.Type == .Error)
{
if (entry.Data.Bofa != null)
{
entry.Data.Bofa.[Friend]_line = null;
delete entry.Data.Bofa;
}
pErrors.Add(line);
continue;
}
@ -178,6 +182,12 @@ class BofaParser
typeName = "Array";
case "o":
typeName = "Object";
case "rgb":
typeName = "RBG";
case "rgba":
typeName = "RBGA";
case "hash":
typeName = "Hash";
default:
toReturn.Type = .Error;
return toReturn; //Unsupported type error
@ -306,6 +316,33 @@ class BofaParser
case "c":
bofaRes.Value.Custom = line;
bofaRes.Type = .Custom;
case "rgb":
bofaRes.Type = .RGB;
var result = AdditionalParsers.ParseRGB(line);
if(result case .Err)
{
toReturn.Type = .Error;
return toReturn;
}
bofaRes.Value.Rgb = result.Value;
case "rgba":
bofaRes.Type = .RGBA;
var result = AdditionalParsers.ParseRGBA(line);
if(result case .Err)
{
toReturn.Type = .Error;
return toReturn;
}
bofaRes.Value.Rgba = result.Value;
case "hash":
bofaRes.Type = .Hash;
var result = AdditionalParsers.ParseHash(line);
if(result case .Err)
{
toReturn.Type = .Error;
return toReturn;
}
bofaRes.Value.Hash = result.Value;
default: //Unknown type
toReturn.Type = .Error;
return toReturn;

View file

@ -2,6 +2,7 @@ namespace Bofa;
using System;
using System.Collections;
using System.Security.Cryptography;
[Union]
struct BofaValue
@ -18,4 +19,7 @@ struct BofaValue
public StringView Custom;
public Dictionary<StringView, Bofa> Object;
public List<Bofa> Array;
public uint8[3] Rgb;
public uint8[4] Rgba;
public SHA256Hash Hash;
}

View file

@ -510,4 +510,26 @@ namespace System.Collections
return true;
}
}
}
namespace System.Security.Cryptography
{
extension SHA256Hash : IBofaParseable
{
public bool Serialize(Bofa.Bofa pTarget)
{
pTarget.Type = .Hash;
pTarget.Typename = "Hash";
pTarget.Value.Hash = (.)this;
return true;
}
public bool Deserialize(Bofa.Bofa pInput) mut
{
if(pInput.Type != .Hash)
return false;
this = pInput.Value.Hash;
return true;
}
}
}

View file

@ -0,0 +1,63 @@
namespace Bofa.Parser;
using System;
using System.Security.Cryptography;
class AdditionalParsers
{
public static Result<uint8[3]> ParseRGB(StringView pValue)
{
uint8[3] toReturn = .(0,0,0);
var parts = pValue.Split(' ');
int idx = 0;
for(var part in parts)
{
defer {idx = idx + 1;}
var res = uint8.Parse(part, .Hex);
if(res case .Err)
return .Err;
if(idx == toReturn.Count)
return .Err;
toReturn[idx] = res.Value;
}
return .Ok(toReturn);
}
public static Result<uint8[4]> ParseRGBA(StringView pValue)
{
uint8[4] toReturn = .(0,0,0,0);
var parts = pValue.Split(' ');
int idx = 0;
for(var part in parts)
{
defer {idx = idx + 1;}
var res = uint8.Parse(part, .HexNumber);
if(res case .Err)
return .Err;
if(idx == toReturn.Count)
return .Err;
toReturn[idx] = res.Value;
}
return .Ok(toReturn);
}
public static Result<SHA256Hash> ParseHash(StringView pValue)
{
//We currently dont support more than sha256 as hashing algorithms
if(pValue.Contains('-') && !pValue.StartsWith("sha256-"))
return .Err;
var pValue;
if(pValue.StartsWith("sha256-"))
pValue = pValue.Substring("sha256-".Length);
return SHA256Hash.Parse(pValue);
}
}

View file

@ -0,0 +1,7 @@
namespace Bofa.Serialization;
using System;
struct BofaRequiredAttribute : Attribute
{
}

View file

@ -8,6 +8,12 @@ using Bofa;
[AttributeUsage(.Struct | .Class)]
struct BofaSerializeAttribute : Attribute, IOnTypeInit
{
private bool Strict;
public this(bool pStrict = false)
{
Strict = pStrict;
}
[Comptime]
public void OnTypeInit(Type type, Self* prev)
{
@ -71,6 +77,8 @@ struct BofaSerializeAttribute : Attribute, IOnTypeInit
hasIFace = true;
else if(f.FieldType == typeof(double))
hasIFace = true;
else if(f.FieldType == typeof(System.Security.Cryptography.SHA256Hash))
hasIFace = true;
if(!hasIFace)
continue;
@ -81,24 +89,39 @@ struct BofaSerializeAttribute : Attribute, IOnTypeInit
if(f.HasCustomAttribute<BofaIncludeAttribute>() && f.GetCustomAttribute<BofaIncludeAttribute>() case .Ok(let attr))
name = attr.Name;
var required = f.HasCustomAttribute<BofaRequiredAttribute>() || Strict;
serializeString.Append(scope $"""
toAdd = new .();
toAdd.Name = "{name}";
if(!{f.Name}.Serialize(toAdd))
\{
delete toAdd;
return false;
{required ? "return false;" : ""}
\}
else
b.Value.Object.Add("{name}", toAdd);
""");
deserializeString.Append(scope $"""
if(required)
{
deserializeString.Append((scope $"""
if(!b.Value.Object.ContainsKey("{name}"))
return false;
if(!{f.Name}.Deserialize(b.Value.Object["{name}"]))
return false;
"""));
}
else
{
deserializeString.Append(scope $"""
if(b.Value.Object.ContainsKey("{name}"))
{f.Name}.Deserialize(b.Value.Object["{name}"]);
""");
}
}

View file

@ -28,12 +28,17 @@ class Default
a array
b content true
b content true
# 1.1.0
rgba color FF FF FF FF
rgb c 34 67 EF
hash id sha256-218989F4E69B157EA38EBFC3051FB7A0011FBD1B2E4303DCB48D46C10622DA0B
""";
Dictionary<StringView, Bofa> output = new .();
List<int64> errors = new .();
BofaParser.Parse(content, output, errors);
Test.Assert(output.Count == 9);
Test.Assert(output.Count == 12);
Test.Assert(errors.Count == 0);
DeleteDictionaryAndValues!(output);

View file

@ -10,10 +10,11 @@ using Bofa.Serialization;
class Serialization
{
public int anInteger = 1;
public String aString = "Value";
public String aString = new .("Value") ~ delete _;
public List<String> listOfStrings = new .() ~ DeleteContainerAndItems!(_);
public int anotherOne = 0;
[Test]
[Test(Name="Serialize")]
public static void Test()
{
Serialization s = scope .();
@ -24,4 +25,26 @@ class Serialization
}
[Test(Name="Deserialize")]
public static void Test_Deserialize()
{
Serialization s = scope .();
String content = """
o name
i anInteger 35
l aString this is a string
a listOfStrings
""";
Dictionary<StringView, Bofa> output = new .();
List<int64> errors = new .();
BofaParser.Parse(content, output, errors);
Test.Assert(s.Deserialize(output["name"]));
DeleteDictionaryAndValues!(output);
delete errors;
}
}

View file

@ -0,0 +1,36 @@
namespace Bofa.Testing;
/*
Testing the new functionality that comes with 1.1.0
*/
using System;
using System.Collections;
class Update_1_1_0
{
[Test(Name="1.1.0 - New Types")]
public static void Test_Types()
{
String content = """
# Should work
rgba alpha FF FF FF FF
rgb nonAlpha 34 67 EF
hash id sha256-218989F4E69B157EA38EBFC3051FB7A0011FBD1B2E4303DCB48D46C10622DA0B
hash without 218989F4E69B157EA38EBFC3051FB7A0011FBD1B2E4303DCB48D46C10622DA0B
# Should error
hash id md5-218989F4E69B157EA38EBFC3051FB7A0011FBD1B2E4303DCB48D46C10622DA0B
rgba alpha ZZ ZZ ZZ ZZ
rgb nonAlpha DE AD BE EF
""";
Dictionary<StringView, Bofa> output = new .();
List<int64> errors = new .();
BofaParser.Parse(content, output, errors);
Test.Assert(output.Count == 4);
Test.Assert(errors.Count == 3);
DeleteDictionaryAndValues!(output);
delete errors;
}
}

View file

@ -13,5 +13,10 @@ enum EBofaType
Boolean, //8bit true or false
Object, // Key-Value container
Array, //Numeric container
Custom //String with a type attached to it
Custom, //String with a type attached to it
//1.1.0
RGB, //3 * 8bit
RGBA, //4 * 8bit
Hash, //Sha256
}