Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add general documentation #50

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
eff6946
Ensure generated attribute honors nullable & required properties
bruno-f-cruz Nov 6, 2024
c001718
Add bonsai environment
bruno-f-cruz Dec 27, 2024
39727d4
Add introduction
bruno-f-cruz Dec 27, 2024
e5a12cb
Add scaffold for articles
bruno-f-cruz Dec 27, 2024
7021f47
Add Extensions to example workflow
bruno-f-cruz Dec 27, 2024
9784f08
Add dynamic-class example
bruno-f-cruz Dec 27, 2024
070f8fc
Ignore layout files inside workflow examples
bruno-f-cruz Dec 27, 2024
56f11bb
Add local dotnet tool manifest and register Bonsai.Sgen
bruno-f-cruz Dec 27, 2024
44cf885
Add getting started instructions
bruno-f-cruz Dec 27, 2024
9fb6072
Add Yaml and Json package references to Extensions csproj
bruno-f-cruz Dec 27, 2024
6f3cf6b
Change title
bruno-f-cruz Dec 27, 2024
f96b9ac
Ignore environment temp files
bruno-f-cruz Dec 27, 2024
7331a2c
Add environment package requirements
bruno-f-cruz Dec 27, 2024
8597727
Add short sgen example
bruno-f-cruz Dec 27, 2024
adea528
Add person json schema and generated bonsai classes
bruno-f-cruz Dec 27, 2024
64a0540
Move section
bruno-f-cruz Dec 27, 2024
a4ad4ff
Add usage examples
bruno-f-cruz Dec 27, 2024
457343e
Remove unecessary label
bruno-f-cruz Dec 27, 2024
d3207dd
Refactor file names
bruno-f-cruz Dec 27, 2024
99d973f
Add array manipulation example
bruno-f-cruz Dec 27, 2024
2e37736
Add nullable example
bruno-f-cruz Dec 27, 2024
7561403
Add required field section
bruno-f-cruz Dec 27, 2024
e1fab34
Add documentation on Unions
bruno-f-cruz Dec 28, 2024
e98f70c
Add section on partials
bruno-f-cruz Dec 28, 2024
5f0262e
Add x-abstract documentation
bruno-f-cruz Dec 28, 2024
fece6dc
Add (de)serialization examples
bruno-f-cruz Dec 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .bonsai/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Bonsai scripting files
.vs
.vscode
bin
obj
Packages
Bonsai.exe.WebView2
*.bin
*.avi
*.dll
*.exe
*.exe.settings
*.bonsai.layout
72 changes: 72 additions & 0 deletions .bonsai/Bonsai.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Packages>
<Package id="Bonsai" version="2.8.5" />
<Package id="Bonsai.Core" version="2.8.5" />
<Package id="Bonsai.Design" version="2.8.5" />
<Package id="Bonsai.Editor" version="2.8.5" />
<Package id="Bonsai.Scripting.Expressions" version="2.8.0" />
<Package id="Bonsai.Scripting.Expressions.Design" version="2.8.0" />
<Package id="Bonsai.System" version="2.8.1" />
<Package id="jacobslusser.ScintillaNET" version="3.6.3" />
<Package id="Markdig" version="0.18.1" />
<Package id="Microsoft.Web.WebView2" version="1.0.1823.32" />
<Package id="Newtonsoft.Json" version="13.0.3" />
<Package id="Rx-Core" version="2.2.5" />
<Package id="Rx-Interfaces" version="2.2.5" />
<Package id="Rx-Linq" version="2.2.5" />
<Package id="Rx-PlatformServices" version="2.2.5" />
<Package id="SvgNet" version="3.3.3" />
<Package id="System.Buffers" version="4.5.1" />
<Package id="System.Linq.Dynamic" version="1.0.7" />
<Package id="System.Memory" version="4.5.5" />
<Package id="System.Numerics.Vectors" version="4.5.0" />
<Package id="System.Resources.Extensions" version="8.0.0" />
<Package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3" />
<Package id="YamlDotNet" version="13.1.1" />
</Packages>
<AssemblyReferences>
<AssemblyReference assemblyName="Bonsai" />
<AssemblyReference assemblyName="Bonsai.Core" />
<AssemblyReference assemblyName="Bonsai.Design" />
<AssemblyReference assemblyName="Bonsai.Editor" />
<AssemblyReference assemblyName="Bonsai.Scripting.Expressions" />
<AssemblyReference assemblyName="Bonsai.Scripting.Expressions.Design" />
<AssemblyReference assemblyName="Bonsai.System" />
</AssemblyReferences>
<AssemblyLocations>
<AssemblyLocation assemblyName="Bonsai" processorArchitecture="MSIL" location="Packages/Bonsai.2.8.5/lib/net48/Bonsai.exe" />
<AssemblyLocation assemblyName="Bonsai.Core" processorArchitecture="MSIL" location="Packages/Bonsai.Core.2.8.5/lib/net462/Bonsai.Core.dll" />
<AssemblyLocation assemblyName="Bonsai.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Design.2.8.5/lib/net462/Bonsai.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.Editor" processorArchitecture="MSIL" location="Packages/Bonsai.Editor.2.8.5/lib/net472/Bonsai.Editor.dll" />
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions" processorArchitecture="MSIL" location="Packages/Bonsai.Scripting.Expressions.2.8.0/lib/net462/Bonsai.Scripting.Expressions.dll" />
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Scripting.Expressions.Design.2.8.0/lib/net462/Bonsai.Scripting.Expressions.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.System" processorArchitecture="MSIL" location="Packages/Bonsai.System.2.8.1/lib/net462/Bonsai.System.dll" />
<AssemblyLocation assemblyName="Markdig" processorArchitecture="MSIL" location="Packages/Markdig.0.18.1/lib/net40/Markdig.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Core" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.Core.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.WinForms" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.WinForms.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Wpf" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.Wpf.dll" />
<AssemblyLocation assemblyName="Newtonsoft.Json" processorArchitecture="MSIL" location="Packages/Newtonsoft.Json.13.0.3/lib/net45/Newtonsoft.Json.dll" />
<AssemblyLocation assemblyName="ScintillaNET" processorArchitecture="MSIL" location="Packages/jacobslusser.ScintillaNET.3.6.3/lib/net40/ScintillaNET.dll" />
<AssemblyLocation assemblyName="SVG" processorArchitecture="MSIL" location="Packages/SvgNet.3.3.3/lib/net462/SVG.dll" />
<AssemblyLocation assemblyName="System.Buffers" processorArchitecture="MSIL" location="Packages/System.Buffers.4.5.1/lib/net461/System.Buffers.dll" />
<AssemblyLocation assemblyName="System.Linq.Dynamic" processorArchitecture="MSIL" location="Packages/System.Linq.Dynamic.1.0.7/lib/net40/System.Linq.Dynamic.dll" />
<AssemblyLocation assemblyName="System.Memory" processorArchitecture="MSIL" location="Packages/System.Memory.4.5.5/lib/net461/System.Memory.dll" />
<AssemblyLocation assemblyName="System.Numerics.Vectors" processorArchitecture="MSIL" location="Packages/System.Numerics.Vectors.4.5.0/lib/net46/System.Numerics.Vectors.dll" />
<AssemblyLocation assemblyName="System.Reactive.Core" processorArchitecture="MSIL" location="Packages/Rx-Core.2.2.5/lib/net45/System.Reactive.Core.dll" />
<AssemblyLocation assemblyName="System.Reactive.Interfaces" processorArchitecture="MSIL" location="Packages/Rx-Interfaces.2.2.5/lib/net45/System.Reactive.Interfaces.dll" />
<AssemblyLocation assemblyName="System.Reactive.Linq" processorArchitecture="MSIL" location="Packages/Rx-Linq.2.2.5/lib/net45/System.Reactive.Linq.dll" />
<AssemblyLocation assemblyName="System.Reactive.PlatformServices" processorArchitecture="MSIL" location="Packages/Rx-PlatformServices.2.2.5/lib/net45/System.Reactive.PlatformServices.dll" />
<AssemblyLocation assemblyName="System.Resources.Extensions" processorArchitecture="MSIL" location="Packages/System.Resources.Extensions.8.0.0/lib/net462/System.Resources.Extensions.dll" />
<AssemblyLocation assemblyName="System.Runtime.CompilerServices.Unsafe" processorArchitecture="MSIL" location="Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/net461/System.Runtime.CompilerServices.Unsafe.dll" />
<AssemblyLocation assemblyName="YamlDotNet" processorArchitecture="MSIL" location="Packages/YamlDotNet.13.1.1/lib/net47/YamlDotNet.dll" />
</AssemblyLocations>
<LibraryFolders>
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-arm64/native" platform="arm64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-arm64/native_uap" platform="arm64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x64/native" platform="x64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x64/native_uap" platform="x64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x86/native" platform="x86" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x86/native_uap" platform="x86" />
</LibraryFolders>
</PackageConfiguration>
8 changes: 8 additions & 0 deletions .bonsai/NuGet.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Gallery" value="Gallery" />
<add key="Bonsai Packages" value="https://www.myget.org/F/bonsai/api/v3/index.json" />
<add key="Community Packages" value="https://www.myget.org/F/bonsai-community/api/v3/index.json" />
</packageSources>
</configuration>
1 change: 1 addition & 0 deletions .bonsai/setup.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
powershell -ExecutionPolicy Bypass -File .\setup.ps1
21 changes: 21 additions & 0 deletions .bonsai/setup.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Push-Location $PSScriptRoot
if (!(Test-Path "./Bonsai.exe")) {
$release = "https://github.com/bonsai-rx/bonsai/releases/latest/download/Bonsai.zip"
$configPath = "./Bonsai.config"
if (Test-Path $configPath) {
[xml]$config = Get-Content $configPath
$bootstrapper = $config.PackageConfiguration.Packages.Package.where{$_.id -eq 'Bonsai'}
if ($bootstrapper) {
$version = $bootstrapper.version
$release = "https://github.com/bonsai-rx/bonsai/releases/download/$version/Bonsai.zip"
}
}
Invoke-WebRequest $release -OutFile "temp.zip"
Move-Item -Path "NuGet.config" "temp.config" -ErrorAction SilentlyContinue
Expand-Archive "temp.zip" -DestinationPath "." -Force
Move-Item -Path "temp.config" "NuGet.config" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "temp.zip"
Remove-Item -Path "Bonsai32.exe"
}
& .\Bonsai.exe --no-editor
Pop-Location
13 changes: 13 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"bonsai.sgen": {
"version": "0.4.0",
"commands": [
"bonsai.sgen"
],
"rollForward": false
}
}
}
2 changes: 1 addition & 1 deletion Bonsai.Sgen/CSharpClassTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public override void BuildType(CodeTypeDeclaration type)
nameof(JsonPropertyAttribute.Required),
new CodeFieldReferenceExpression(
new CodeTypeReferenceExpression(typeof(Required)),
nameof(Required.Always))));
property.IsNullable ? nameof(Required.AllowNull) : nameof(Required.Always))));
}
propertyDeclaration.CustomAttributes.Add(jsonProperty);
}
Expand Down
111 changes: 111 additions & 0 deletions docs/articles/advanced-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
uid: advanced-usage
---


## Unions

In the previous examples, we have seen how create object properties of a single type. However, in practice, data structures' fields can often be represented by one of several types. We have actually seen a special case of this behavior in the previous nullable example, where a field can be either a value of a given type or `null` (or an Union between type `T` and `null`).

Similarly, `json-schema` allows union types to be defined using the `oneOf` keyword. For example, consider the following schema:

```json
{
"title": "MyPet",
"type": "object",
"properties": {
"FooProperty": {
"oneOf": [
{ "type": "string" },
{ "type": "number" }
]
}
}
}
```

If we run `Bonsai.Sgen` on this schema, we will get the following signature for the `FooProperty` property:

```csharp
public object FooProperty
```

This is because while the `oneOf` keyword is supported by the `Bonsai.Sgen` tool, for statically typed languages like `C#` and `Bonsai`, we need to know the exact type of the property at compile time. As a result, we opt to "up-cast" the property to the most general type that can represent all the possible types in the union (`object`). It is up to the user to down-cast the property to the correct type at runtime.


## Tagged Unions

At this point, you might be wondering if there is a way to represent union types in a more type-safe way in json-schema. The answer is yes, and the way to do it is by using [`discriminated unions`](https://en.wikipedia.org/wiki/Tagged_union) (or `tagged union`). The syntax for discriminated unions is not supported by vanilla `json-schema`, but it is supported by the [`OpenAPI` standard](https://swagger.io/docs/specification/v3_0/data-models/inheritance-and-polymorphism/#discriminator), which is a superset of `json-schema`. The key idea behind discriminated unions is to add a `discriminator` field to the schema that specifies the property that will be used to determine the type of the object at runtime.

For example, a `Pet` object that can be either a `Dog` or a `Cat` can be represented as follows:

[person](~/workflows/person-and-discriminated-pets.json)

```json
"Pet": {
"discriminator": {
"mapping": {
"cat": "#/definitions/Cat",
"dog": "#/definitions/Dog"
},
"propertyName": "pet_type"
},
"oneOf": [
{
"$ref": "#/definitions/Dog"
},
{
"$ref": "#/definitions/Cat"
}
]
}
```

In `C#`, `Bonsai.Sgen` will generate a root type `Pet` that will be inherited by the `Dog` and `Cat` types (since in the worst case scenario, the discriminated property must be shared). The `Pet` type will have a `pet_type` property that will be used to downcast to the proper type at runtime. At this point we can open our example in `Bonsai` and see how the `Pet` type is represented in the workflow.

As you can see below, we still get a `Pet` type. Better than `object` but still not a `Dog` or `Cat` type. Fortunately, `Bonsai.Sgen` will generate an operator that can be used to filter and downcast the `Pet` objects to the correct type at runtime. These are called `Match<T>` operators. After adding a `MatchPet` to our workflow we can select the desired target type which will allow us access to the properties of the `Dog` or `Cat` type. Conversely, we can also upcast a `Dog` or `Cat` to a `Pet` leaving the `MatchPet` operator's `Type` property empty.

:::workflow
![Discriminated Unions](~/workflows/person-pet-discriminated-union.bonsai)
:::

> [!Important]
> In is general advisable to use references in the `oneOf` syntax. Not only does this decision make your `json-schema` significantly smaller, it will also help `Bonsai.Sgen` generate the correct class hierarchy if multiple unions are present in the schema. If you use inline objects, `Bonsai.Sgen` will likely have to generate a new root class for each union, which can lead to a lot of duplicated code and a more complex object hierarchy.



## Extending generated code with `partial` classes

Since `Bonsai.Sgen` will generate proper `class` for each object in the schema, it is possible to use these types to create custom operators and methods using the `Scriping Extensions` feature of `Bonsai`. However, sometimes we may want to extend the features of the generated classes directly...

For those that inspected the general `C#` code, you will notice that all classes are marked as [`partial`](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods). This is a feature of `C#` that allows a class to be split. This was a deliberate design choice to allow users to extend the generated code. However, because it is usually always a bad idea to modify generated code directly (e.g. we may want to regenerate it in the future), `partial` classes allows modification to be made in a separate file.

Suppose we want to sum `Cats`, we can overload the operator with a small method in a separate file::

```csharp
namespace PersonAndDiscriminatedPets
{
partial class Cat{
public static Cat operator +(Cat c1, Cat c2)
{
return new Cat
{
CanMeow = c1.CanMeow || c2.CanMeow,
Age = c1.Age + c2.Age
};
}
}
}
```

In `Bonsai`, we can now use the `Add` operator to sum `Cats`:


:::workflow
![Discriminated Unions](~/workflows/sum-cats.bonsai)
:::


## Other supported tags

- `x-abstract`: This tag is used to mark a class as abstract. An abstract class will not be generated as an operator in Bonsai. This may be useful for root classes of unions that may never need to be manipulated in Bonsai.
Loading
Loading