You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/docs/specta/rfc/flightscience.mdx
+85Lines changed: 85 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1730,3 +1730,88 @@ I also took this opportunity to simplify some stuff:
1730
1730
This all leads to a much simpler system, and it also means the job of preventing stack overflows is on Specta's core not the language exporters which is really nice for reducing bugs.
1731
1731
1732
1732
Work for this was done on: [#483](https://github.com/specta-rs/specta/pull/483)
1733
+
1734
+
## Semantic Types
1735
+
1736
+
The work for this was done on: [#203](https://github.com/specta-rs/specta/issues/203)
1737
+
1738
+
I did previously call this nuanced types but that's not really true anymore.
1739
+
1740
+
### Getting terminology right
1741
+
1742
+
The original nuanced types feature confused two different Specta features which ended up being quite related so I wanted to clarify the terminology.
1743
+
1744
+
Nuanced types has the goal of allowing a language exporter to differentiate which Rust type it's targeting. The primary usecase for this is Specta Rust, as the core `DataType` model expresses `BTreeMap`, `HashMap`, `index_map::IndexMap`, etc all as `DataType::Map`. For a Rust exporter to be good we want to be able to know *which map*.
1745
+
1746
+
This was solved by making *everything* bar Rust primitives (`str`, `bool`, `char`, etc.) a Specta `NamedDataType` but just marked as inline so they disappear, and don't end up in the final bindings file.
1747
+
1748
+
Semantic types aims to make `BigInt`, `Date`, `Uint8Array`, etc. work with Specta. This goes furthur than just Rust types as these have a specific Javascript runtime component. Eg. A `Date` may be serialized to JSON as a `string` so we need to revive using `new Date(dateStrFromJson)`.
1749
+
1750
+
The solution to this was `specta_typescript::semantic`.
1751
+
1752
+
### Semantic Types
1753
+
1754
+
I did a massive writeup [in a comment for #203](https://github.com/specta-rs/specta/issues/203#issuecomment-4387573925) to outline my conclusion here. But in essence:
- *[1]* - BigInt's are explained later on and are less cut and dry.
1766
+
- *[2]* - These types are actually handled by `JSON.stringify` for us so we don't need to do any special code.
1767
+
1768
+
### `Date`, `Uint8Array` and `URL`
1769
+
1770
+
The core observation of these is that the JSON type and Rust serialization is all good. All we need is a way to change the Specta type and generate some runtime JS code.
1771
+
1772
+
So that's where [`specta_typescript::semantic::Configuration`](https://docs.rs/specta-typescript/latest/specta_typescript/semantic/struct.Configuration.html) comes in.
1773
+
1774
+
Internally it defines a set of default "rules" (with the option for the user to provide custom ones) which look similar to the following:
Using `specta_typescript::semantic::Configuration::default()` we provide the following default rules:
1788
+
- [`bytes::Bytes`](https://docs.rs/bytes/latest/bytes/struct.Bytes.html) to become [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
1789
+
- [`bytes::BytesMut`](https://docs.rs/bytes/latest/bytes/struct.BytesMut.html) to become [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
1790
+
- [`url::Url`](https://docs.rs/url/latest/url/struct.Url.html) to become [`Url`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL)
1791
+
- [`chrono::DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) to become [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
1792
+
- [`chrono::NaiveDate`](https://docs.rs/chrono/latest/chrono/struct.NaiveDate.html) to become [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
1793
+
- [`jiff::Timestamp`](https://docs.rs/jiff/latest/jiff/struct.Timestamp.html) to become [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
1794
+
- [`jiff::civil::Date`](https://docs.rs/jiff/latest/jiff/civil/struct.Date.html) to become [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
1795
+
1796
+
Then frameworks like Tauri Specta can use with the following APIs:
- Takes in a `DataType`, and `js_ident` (for example `v`), then it will apply the rules returning an updated `DataType` if required and a runtime string.
1801
+
- The runtime string can be used like `outputFromRust.then((v) => {js_runtime_str})`.
1802
+
- This generates something like `outputFromRust.then((v) => { date_field: new Date(v) })`. It also handles nesting, arrays, etc.
1803
+
- You can use `apply_serialize` or `apply_deserialize` depending on the direction as the runtime needs to be different.
1804
+
1805
+
This all comes together to allow these types to work. I have implemented this into Tauri Specta in [#219](https://github.com/specta-rs/tauri-specta/pull/219) and TauRPC in [#22](https://github.com/fltsci/TauRPC/pull/22).
1806
+
1807
+
I have some concerns about this approach with [rspc](https://github.com/specta-rs/rspc) as by generating JS code that depends on the object shape, it requires your frontend and backend to be versioned in perfect sync. For Tauri this is how it will always be as the JS and Rust is shipped in the same binary, but for people building web APIs this is not usually the case. I have ideas for solving this but it is out of scope for now and rspc is not currently maintained so it's not a massive concern.
1808
+
1809
+
### BigInt and special-float support in Tauri
1810
+
1811
+
TODO:
1812
+
- Is hard - Explain how `IpcResponse` is effectivly seal from HTTP headers
0 commit comments