Compare commits
23 commits
Author | SHA1 | Date | |
---|---|---|---|
26d18bb81a | |||
3d66292a55 | |||
b2161b3011 | |||
65eb3c8d95 | |||
a5cc909153 | |||
beb1c6ca92 | |||
f6e2991ae1 | |||
dae6c8f9dc | |||
4d7c342dbd | |||
4076974f6b | |||
95b06f1465 | |||
f9647915a5 | |||
667d74ddcf | |||
41f8c6b41d | |||
5d20dfd967 | |||
cbaa413cd0 | |||
f4a2fee0d7 | |||
6572a85cfc | |||
dbc38e0195 | |||
c8629face9 | |||
fab910e2fc | |||
edcb459ad1 | |||
ac7bb9191d |
16 changed files with 343 additions and 16 deletions
11
.forgejo/workflows/test.yml
Normal file
11
.forgejo/workflows/test.yml
Normal 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
22
CHANGELOG.md
Normal 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
|
34
README.md
34
README.md
|
@ -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
13
migration-guide.md
Normal 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
13
pkkg
Normal 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
|
29
src/Bofa.bf
29
src/Bofa.bf
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
63
src/Parser/AdditionalParsers.bf
Normal file
63
src/Parser/AdditionalParsers.bf
Normal 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);
|
||||
}
|
||||
|
||||
}
|
7
src/Serialization/BofaRequiredAttribute.bf
Normal file
7
src/Serialization/BofaRequiredAttribute.bf
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Bofa.Serialization;
|
||||
|
||||
using System;
|
||||
|
||||
struct BofaRequiredAttribute : Attribute
|
||||
{
|
||||
}
|
|
@ -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}"]);
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
36
src/Testing/Update_1_1_0.bf
Normal file
36
src/Testing/Update_1_1_0.bf
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Add table
Reference in a new issue