diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..24a5f8e --- /dev/null +++ b/CHANGELOG.md @@ -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, Dictionary)__ + - _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 \ No newline at end of file diff --git a/README.md b/README.md index 58d3817..fe191bb 100644 --- a/README.md +++ b/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 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)` 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 \ No newline at end of file diff --git a/migration-guide.md b/migration-guide.md new file mode 100644 index 0000000..69c722b --- /dev/null +++ b/migration-guide.md @@ -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` instead. +``` + o object + l line of text + n value 34354545.6 +``` \ No newline at end of file diff --git a/pkkg b/pkkg new file mode 100644 index 0000000..44efee0 --- /dev/null +++ b/pkkg @@ -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 \ No newline at end of file diff --git a/src/Bofa.bf b/src/Bofa.bf index 1dc3fb3..d8c8032 100644 --- a/src/Bofa.bf +++ b/src/Bofa.bf @@ -14,6 +14,27 @@ class Bofa public StringView Typename; public BofaValue Value; + public static bool Deserialize(ref T pType, Dictionary 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 diff --git a/src/BofaParser.bf b/src/BofaParser.bf index 64c3f7e..afe104e 100644 --- a/src/BofaParser.bf +++ b/src/BofaParser.bf @@ -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; diff --git a/src/BofaValue.bf b/src/BofaValue.bf index 2399e0e..48bbe85 100644 --- a/src/BofaValue.bf +++ b/src/BofaValue.bf @@ -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 Object; public List Array; + public uint8[3] Rgb; + public uint8[4] Rgba; + public SHA256Hash Hash; } \ No newline at end of file diff --git a/src/Extension.bf b/src/Extension.bf index 606e67d..ecf4a68 100644 --- a/src/Extension.bf +++ b/src/Extension.bf @@ -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; + } + } } \ No newline at end of file diff --git a/src/Parser/AdditionalParsers.bf b/src/Parser/AdditionalParsers.bf new file mode 100644 index 0000000..b44c62e --- /dev/null +++ b/src/Parser/AdditionalParsers.bf @@ -0,0 +1,63 @@ +namespace Bofa.Parser; + +using System; +using System.Security.Cryptography; + +class AdditionalParsers +{ + public static Result 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 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 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); + } + +} \ No newline at end of file diff --git a/src/Serialization/BofaRequiredAttribute.bf b/src/Serialization/BofaRequiredAttribute.bf new file mode 100644 index 0000000..fd00208 --- /dev/null +++ b/src/Serialization/BofaRequiredAttribute.bf @@ -0,0 +1,7 @@ +namespace Bofa.Serialization; + +using System; + +struct BofaRequiredAttribute : Attribute +{ +} \ No newline at end of file diff --git a/src/Serialization/BofaSerializeAttribute.bf b/src/Serialization/BofaSerializeAttribute.bf index c3a17f6..6a36e09 100644 --- a/src/Serialization/BofaSerializeAttribute.bf +++ b/src/Serialization/BofaSerializeAttribute.bf @@ -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() && f.GetCustomAttribute() case .Ok(let attr)) name = attr.Name; + var required = f.HasCustomAttribute() || 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}"]); + """); + } + } diff --git a/src/Testing/Default.bf b/src/Testing/Default.bf index 9b708c7..898cf14 100644 --- a/src/Testing/Default.bf +++ b/src/Testing/Default.bf @@ -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 output = new .(); List errors = new .(); BofaParser.Parse(content, output, errors); - Test.Assert(output.Count == 9); + Test.Assert(output.Count == 12); Test.Assert(errors.Count == 0); DeleteDictionaryAndValues!(output); diff --git a/src/Testing/Serialization.bf b/src/Testing/Serialization.bf index 6c21d96..5bc8ab9 100644 --- a/src/Testing/Serialization.bf +++ b/src/Testing/Serialization.bf @@ -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 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 output = new .(); + List errors = new .(); + BofaParser.Parse(content, output, errors); + + Test.Assert(s.Deserialize(output["name"])); + + DeleteDictionaryAndValues!(output); + delete errors; + + } + } \ No newline at end of file diff --git a/src/Testing/Update_1_1_0.bf b/src/Testing/Update_1_1_0.bf new file mode 100644 index 0000000..3a3da24 --- /dev/null +++ b/src/Testing/Update_1_1_0.bf @@ -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 output = new .(); + List errors = new .(); + BofaParser.Parse(content, output, errors); + + Test.Assert(output.Count == 4); + Test.Assert(errors.Count == 3); + + DeleteDictionaryAndValues!(output); + delete errors; + } +} \ No newline at end of file diff --git a/src/eBofaType.bf b/src/eBofaType.bf index d3dab82..d4dc1d7 100644 --- a/src/eBofaType.bf +++ b/src/eBofaType.bf @@ -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 } \ No newline at end of file