Skip to content

Commit

Permalink
Export statics
Browse files Browse the repository at this point in the history
closes #95
  • Loading branch information
oscartbeaumont committed Jun 2, 2024
1 parent 9f36820 commit 785f393
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 5 deletions.
12 changes: 12 additions & 0 deletions docs/v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ export ... = {};

Any type implemented using the [`Type`](https://docs.rs/specta/latest/specta/derive.Type.html) derive macro will meet this requirement.

## Constants

It may be useful to export a constant from your Rust into your Typescript. You can do this like the following:

```rust
let builder = ts::builder()
// < your commands and events are probaly here
.types(StaticCollection::default().register("myConstant", 42)); // < call `register` as much as you want.
```

This value must implement [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html).

## File header

It's very common that your are using a linting or formatting tool on your codebase and it's likely that the output of Tauri Specta will not match your style. You can configure the header of the file like the following to solve this:
Expand Down
10 changes: 9 additions & 1 deletion examples/app/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ pub struct EmptyEvent;
#[derive(Type)]
pub struct Custom(String);

#[derive(Type)]
pub struct Testing {
a: String,
}

fn main() {
let (invoke_handler, register_events) = {
let builder = ts::builder()
Expand All @@ -124,7 +129,10 @@ fn main() {
])
.events(tauri_specta::collect_events![DemoEvent, EmptyEvent])
.types(TypeCollection::default().register::<Custom>())
.config(specta::ts::ExportConfig::default().formatter(specta::ts::formatter::prettier));
.config(specta::ts::ExportConfig::default().formatter(specta::ts::formatter::prettier))
.types(TypeCollection::default().register::<Testing>())
.statics(StaticCollection::default().register("universalConstant", 42))
.header("/* These are my Tauri Specta Bindings! */");

#[cfg(debug_assertions)]
let builder = builder.path("../src/bindings.ts");
Expand Down
10 changes: 10 additions & 0 deletions examples/app/src/bindings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/* These are my Tauri Specta Bindings! */
// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually.

/** user-defined commands **/

export const commands = {
/**
* HELLO
Expand Down Expand Up @@ -50,6 +53,8 @@ try {
}
}

/** user-defined events **/

export const events = __makeEvents__<{
demoEvent: DemoEvent,
emptyEvent: EmptyEvent
Expand All @@ -58,6 +63,10 @@ demoEvent: "demo-event",
emptyEvent: "empty-event"
})

/** user-defined statics **/

const universalConstant = 42 as const;

/** user-defined types **/

export type Custom = string
Expand All @@ -66,6 +75,7 @@ export type EmptyEvent = null
export type MyError = { type: "IoError" } | { type: "AnotherError"; data: string }
export type MyError2 = { type: "IoError"; data: string }
export type MyStruct = { some_field: string }
export type Testing = { a: string }

/** tauri-specta globals **/

Expand Down
11 changes: 10 additions & 1 deletion src/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ impl ExportLanguage for Language {
commands: &[FunctionDataType],
events: &[EventDataType],
type_map: &TypeMap,
statics: &StaticCollection,
cfg: &ExportConfig,
) -> Result<String, Self::Error> {
let dependant_types = type_map
Expand All @@ -122,6 +123,14 @@ impl ExportLanguage for Language {
.collect::<Result<Vec<_>, _>>()
.map(|v| v.join("\n"))?;

js_ts::render_all_parts::<Self>(commands, events, type_map, cfg, &dependant_types, GLOBALS)
js_ts::render_all_parts::<Self>(
commands,
events,
type_map,
statics,
cfg,
&dependant_types,
GLOBALS,
)
}
}
35 changes: 34 additions & 1 deletion src/js_ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use heck::ToLowerCamelCase;
use indoc::formatdoc;
use specta::{function::FunctionDataType, ts, ts::ExportError, DataType, TypeMap};

use crate::{EventDataType, ExportLanguage, ItemType};
use crate::{EventDataType, ExportLanguage, ItemType, StaticCollection};

pub const DO_NOT_EDIT: &str = "// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually.";
const CRINGE_ESLINT_DISABLE: &str = "/* eslint-disable */
Expand All @@ -16,21 +16,54 @@ pub fn render_all_parts<T: ExportLanguage<Config = specta::ts::ExportConfig>>(
commands: &[FunctionDataType],
events: &[EventDataType],
type_map: &TypeMap,
statics: &StaticCollection,
cfg: &ExportConfig,
dependant_types: &str,
globals: &str,
) -> Result<String, T::Error> {
let commands = T::render_commands(commands, type_map, cfg)?;
let events = T::render_events(events, type_map, cfg)?;

let statics = statics
.statics
.iter()
.map(|(name, value)| {
let mut as_const = None;
match &value {
serde_json::Value::Null => {}
serde_json::Value::Bool(_)
| serde_json::Value::Number(_)
| serde_json::Value::String(_)
| serde_json::Value::Array(_)
| serde_json::Value::Object(_) => as_const = Some(" as const"),
}

format!(
"const {name} = {}{};",
serde_json::to_string(&value)
.expect("failed to serialize from `serde_json::Value`"),
as_const.unwrap_or("")
)
})
.collect::<Vec<_>>()
.join("\n");

Ok(formatdoc! {
r#"
{DO_NOT_EDIT}
/** user-defined commands **/
{commands}
/** user-defined events **/
{events}
/** user-defined statics **/
{statics}
/** user-defined types **/
{dependant_types}
Expand Down
23 changes: 22 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,11 @@ mod js_ts;

mod event;
mod manager_ext;
mod statics;

pub use event::*;
pub use manager_ext::*;
pub use statics::StaticCollection;

pub type CollectCommandsTuple<TInvokeHandler> =
(specta::function::CollectFunctionsResult, TInvokeHandler);
Expand Down Expand Up @@ -179,6 +181,7 @@ pub trait ExportLanguage: 'static {
commands: &[FunctionDataType],
events: &[EventDataType],
type_map: &TypeMap,
statics: &StaticCollection,
cfg: &ExportConfig<Self::Config>,
) -> Result<String, Self::Error>;
}
Expand Down Expand Up @@ -263,6 +266,7 @@ pub struct Builder<TLang: ExportLanguage, TCommands, TEvents> {
events: TEvents,
config: ExportConfig<TLang::Config>,
types: TypeCollection,
statics: StaticCollection,
}

impl<TLang, TRuntime> Default for Builder<TLang, NoCommands<TRuntime>, NoEvents>
Expand All @@ -276,6 +280,7 @@ where
events: NoEvents,
config: Default::default(),
types: TypeCollection::default(),
statics: StaticCollection::default(),
}
}
}
Expand All @@ -295,6 +300,7 @@ where
events: self.events,
config: self.config,
types: self.types,
statics: self.statics,
}
}
}
Expand All @@ -310,6 +316,7 @@ where
commands: self.commands,
config: self.config,
types: self.types,
statics: self.statics,
}
}
}
Expand Down Expand Up @@ -339,6 +346,19 @@ where
self
}

/// Allows for exporting static along with your commands and events.
///
/// ```rs
/// use tauri_specta::{ts, StaticCollection};
///
/// ts::build()
/// .statics(StaticCollection::default().register("universalConstant", 42));
/// ```
pub fn statics(mut self, statics: impl Borrow<StaticCollection>) -> Self {
self.statics.extend(statics);
self
}

/// Allows for specifying a custom [`ExportConfiguration`](specta::ts::ExportConfiguration).
pub fn config(mut self, config: TLang::Config) -> Self {
self.config.inner = config;
Expand Down Expand Up @@ -400,6 +420,7 @@ where
config,
events,
types,
statics,
..
} = self;

Expand All @@ -410,7 +431,7 @@ where
let mut type_map = collect_typemap(commands_type_map.iter().chain(events_type_map.iter()));
types.export(&mut type_map);

let rendered = TLang::render(&commands, &events, &type_map, &config)?;
let rendered = TLang::render(&commands, &events, &type_map, &statics, &config)?;

Ok((
format!("{}\n{rendered}", &config.header),
Expand Down
39 changes: 39 additions & 0 deletions src/statics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// TODO: Restrict this to only non-JS/TS languages.

use std::{
borrow::{Borrow, Cow},
collections::HashMap,
};

use serde::Serialize;

/// Define a set of statics which can be included in the exporter
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct StaticCollection {
pub(crate) statics: HashMap<Cow<'static, str>, serde_json::Value>,
}

impl StaticCollection {
/// Join another type collection into this one.
pub fn extend(&mut self, collection: impl Borrow<StaticCollection>) -> &mut Self {
self.statics.extend(
collection
.borrow()
.statics
.iter()
.map(|(k, v)| (k.clone(), v.clone())),
);
self
}

/// Register a static with the collection.
pub fn register<T: Serialize>(
&mut self,
name: impl Into<Cow<'static, str>>,
value: T,
) -> &mut Self {
self.statics
.insert(name.into(), serde_json::to_value(&value).unwrap());
self
}
}
11 changes: 10 additions & 1 deletion src/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ impl ExportLanguage for Language {
commands: &[FunctionDataType],
events: &[EventDataType],
type_map: &TypeMap,
statics: &StaticCollection,
cfg: &ExportConfig,
) -> Result<String, ExportError> {
let dependant_types = type_map
Expand All @@ -116,6 +117,14 @@ impl ExportLanguage for Language {
.collect::<Result<Vec<_>, _>>()
.map(|v| v.join("\n"))?;

js_ts::render_all_parts::<Self>(commands, events, type_map, cfg, &dependant_types, GLOBALS)
js_ts::render_all_parts::<Self>(
commands,
events,
type_map,
statics,
cfg,
&dependant_types,
GLOBALS,
)
}
}

0 comments on commit 785f393

Please sign in to comment.