Skip to content

Commit

Permalink
Improve typescript generation
Browse files Browse the repository at this point in the history
  • Loading branch information
novacrazy committed Nov 20, 2024
1 parent 31e502f commit 778a3fb
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 48 deletions.
53 changes: 47 additions & 6 deletions examples/generate_ts_sdk.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::io::Write as _;

use ts_bindgen::{TypeRegistry, TypeScriptDef};
use ts_bindgen::{TypeRegistry, TypeScriptDef, TypeScriptType};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut registry = TypeRegistry::default();
Expand All @@ -10,23 +10,64 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

client_sdk::api::commands::register_routes(&mut registry);

let mut out = std::fs::File::create("api.ts")?;
let mut models = std::fs::File::create("autogenerated.ts")?;

write!(out, "import type {{ ")?;
write!(models, "import type {{ ")?;

for (idx, name) in registry.external().iter().enumerate() {
if idx > 0 {
write!(out, ", ")?;
write!(models, ", ")?;
}

write!(out, "{name}")?;
write!(models, "{name}")?;
}

write!(
out,
models,
" }} from './models';\nimport {{ command }} from './api';\n\n{}",
registry.display()
)?;

let mut api = std::fs::File::create("api.ts")?;

for group in ["decl", "values", "types"] {
let mut first = true;
let mut len = 0;


if group == "types" {
writeln!(api, "export type {{")?;
} else {
writeln!(api, "export {{")?;
}

for (name, ty) in registry.iter() {
match group {
"decl" if matches!(ty, TypeScriptType::ApiDecl { .. }) => {}
"values" if ty.is_value() && !matches!(ty, TypeScriptType::ApiDecl { .. }) => {}
"types" if !ty.is_value() => {}
_ => continue,
}

if !first {
if len % 5 == 0 {
write!(api, ",\n ")?;
} else {
write!(api, ", ")?;
}
} else {
write!(api, " ")?;
}

first = false;

write!(api, "{}", name)?;

len += 1;
}

write!(api, "\n}} from './autogenerated';\n\n")?;
}

Ok(())
}
4 changes: 2 additions & 2 deletions src/models/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ const MAX_LENGTH: usize = {
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
#[serde(untagged)]
pub enum AuthToken {
/// Bearer token for users
/// Bearer token for users, has a fixed length of 28 bytes.
Bearer(BearerToken),
/// Bot token for bots
/// Bot token for bots, has a fixed length of 48 bytes.
Bot(BotToken),
}

Expand Down
7 changes: 5 additions & 2 deletions src/models/party/prefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::models::Locale;
bitflags2! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PartyPrefsFlags: i32 {
const DEFAULT_FLAGS = 0;
const DEFAULT = 0;
}
}

Expand All @@ -22,7 +22,7 @@ impl From<u64> for PartyPrefsFlags {

impl Default for PartyPrefsFlags {
fn default() -> Self {
Self::DEFAULT_FLAGS
Self::DEFAULT
}
}

Expand All @@ -34,8 +34,11 @@ mod preferences {
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
pub struct PartyPreferences {
/// Party locale (alias `locale`)
#[serde(default, skip_serializing_if = "is_default", alias = "locale")]
pub l: Locale,

/// Party preferences flags (alias `flags`)
#[serde(default, skip_serializing_if = "is_default", alias = "flags")]
pub f: PartyPrefsFlags,
}
4 changes: 2 additions & 2 deletions src/models/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, enum_primitive_derive::Primitive)]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef), ts(max))]
#[repr(u8)]
pub enum RoomKind {
Text = 0,
Expand Down Expand Up @@ -48,7 +48,7 @@ impl From<RoomKind> for RoomFlags {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef), ts(include(RoomKind)))] // include RoomKind for RoomFlags
pub struct Room {
pub id: RoomId,

Expand Down
1 change: 1 addition & 0 deletions src/models/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct Statistics {
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
pub struct RoomStatistics {
/// Total number of messages sent
pub messages: u64,

/// Total number of attachment files sent
Expand Down
2 changes: 1 addition & 1 deletion src/models/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl UserProfile {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef), ts(include(ElevationLevel)))] // include ElevationLevel for UserFlags
pub struct User {
pub id: UserId,
pub username: SmolStr,
Expand Down
36 changes: 34 additions & 2 deletions src/models/user/prefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum_codes! {

enum_codes! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, serde_repr::Serialize_repr, serde_repr::Deserialize_repr)]
#[ts(non_const)]
pub enum Font: u16 = SansSerif {
#[default]
0 = SansSerif,
Expand All @@ -21,7 +22,6 @@ enum_codes! {

// third-party fonts
30 = OpenDyslexic,

31 = AtkinsonHyperlegible,
}
}
Expand Down Expand Up @@ -127,35 +127,67 @@ impl Default for UserPrefsFlags {

pub mod preferences {
decl_newtype_prefs! {
/// Color temperature in Kelvin
Temperature: u16 = 7500u16,

/// Font size in points
FontSize: f32 = 16.0f32,

/// Tab size in spaces
TabSize: u8 = 4u8,

/// Message padding in pixels
Padding: u8 = 16u8,
}
}

/// User preferences
///
/// Field names are shortened to reduce message size
/// when stored in a database or sent over the network.
/// However, fields can still be deserialized using the
/// provided aliases, documented for each field.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
pub struct UserPreferences {
/// User locale (alias `locale`)
#[serde(default, skip_serializing_if = "is_default", alias = "locale")]
pub l: Locale,

/// User preferences flags (alias `flags`)
#[serde(default, skip_serializing_if = "is_default", alias = "flags")]
pub f: UserPrefsFlags,

/// Who can add you as a friend (alias `friend_add`)
#[serde(default, skip_serializing_if = "is_default", alias = "friend_add")]
pub friend: FriendAddability,
pub fr: FriendAddability,

/// Color temperature in Kelvin (alias `temperature`)
#[serde(default, skip_serializing_if = "is_default", alias = "temperature")]
pub temp: preferences::Temperature,

/// Chat font (alias `chat_font`)
#[serde(default, skip_serializing_if = "is_default", alias = "chat_font")]
pub cf: Font,

/// UI font (alias `ui_font`)
#[serde(default, skip_serializing_if = "is_default", alias = "ui_font")]
pub uf: Font,

/// Chat font size in points (alias `chat_font_size`)
#[serde(default, skip_serializing_if = "is_default", alias = "chat_font_size")]
pub cfs: preferences::FontSize,

/// UI font size in points (alias `ui_font_size`)
#[serde(default, skip_serializing_if = "is_default", alias = "ui_font_size")]
pub ufs: preferences::FontSize,

/// message padding in pixels (alias `padding`)
#[serde(default, skip_serializing_if = "is_default", alias = "padding")]
pub pad: preferences::Padding,

/// tab size in spaces (alias `tab_size`)
#[serde(default, skip_serializing_if = "is_default", alias = "tab_size")]
pub tab: preferences::TabSize,
}
Expand Down
29 changes: 22 additions & 7 deletions src/models/util/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,20 @@ macro_rules! bitflags2 {
// at the cost of excluding combinations of flags
if size_of::<Self>() >= 16 {
let bits_name = concat!(stringify!($BitFlags), "Bit");
let raw_name = concat!("Raw", stringify!($BitFlags));

if registry.contains(bits_name) {
return TypeScriptType::Named(stringify!($BitFlags));
return TypeScriptType::Named(raw_name);
}

registry.add_external(stringify!($BitFlags));
registry.add_external(raw_name);

eprintln!(
"Note: Generating TypeScript for {} as bit positions, relying on external type for usage",
"Note: Generating TypeScript type for {} as bit positions, relying on external type {raw_name} for usage",
stringify!($BitFlags)
);

let mut members = Vec::new();

let mut combinations = Vec::new();

$(
Expand Down Expand Up @@ -93,7 +93,22 @@ macro_rules! bitflags2 {
registry.insert(name, ty, doc);
}

return TypeScriptType::Named(stringify!($BitFlags));
// export const $BitFlagsBit_ALL = [...$BitFlagsBit.values()];
registry.insert(concat!(stringify!($BitFlags), "Bit_ALL"), {
let mut bits = Vec::new();

for (name, value) in Self::all().iter_names() {
let v = value.bits();

if (v & (v - 1)) == 0 {
bits.push(TypeScriptType::EnumValue(concat!(stringify!($BitFlags), "Bit"), name));
}
}

TypeScriptType::ArrayLiteral(bits)
}, concat!("All bit positions of ", stringify!($BitFlags)));

return TypeScriptType::Named(raw_name);
}

// regular enum
Expand Down Expand Up @@ -153,8 +168,8 @@ macro_rules! enum_codes {
}
) => {
rkyv_rpc::enum_codes! {
$(#[$meta])*
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
$(#[$meta])*
$vis enum $name: $archived_vis $repr $(= $unknown)? {
$($(#[$variant_meta])* $code = $variant,)*
}
Expand All @@ -170,8 +185,8 @@ macro_rules! enum_codes {
$($(#[$variant_meta:meta])* $code:literal = $variant:ident,)*
}
) => {
$(#[$meta])*
#[cfg_attr(feature = "ts", derive(ts_bindgen::TypeScriptDef))]
$(#[$meta])*
#[repr($repr)]
$vis enum $name {
$($(#[$variant_meta])* $variant = $code,)*
Expand Down
Loading

0 comments on commit 778a3fb

Please sign in to comment.