1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::fs::{File, OpenOptions};
use std::io::{Write, BufWriter};
use std::path::Path;

pub use ts_rs_macros::TS;

pub trait TS {
    /// Declaration of this type, e.g. `interface User { user_id: number, ... }`, if available.
    fn decl() -> Option<String> {
        None
    }
    /// Formats this type.
    /// When using inline, this will return the definition of the type.
    /// Otherwise, it's name is returned (if the type is named)
    fn format(indent: usize, inline: bool) -> String;

    fn dump(out: impl AsRef<Path>) -> std::io::Result<()> {
        let out = out.as_ref();
        let mut file = OpenOptions::new().append(true).create(true).truncate(false).open(out)?;
        let mut writer = BufWriter::new(file);
        writer.write_all(Self::decl().expect("Type has no declaration").as_bytes())?;
        writer.write_all(b"\n\n")?;
        writer.flush()?;
        Ok(())
    }
}

macro_rules! impl_primitives {
    ($($($ty:ty),* => $l:literal),*) => { $($(
        impl TS for $ty {
            fn decl() -> Option<String> { None }
            fn format(_: usize, _: bool) -> String {
                $l.to_owned()
            }
        }
    )*)* };
}

macro_rules! impl_tuples {
    ( impl $($i:ident),* ) => {
        impl<$($i: TS),*> TS for ($($i,)*) {
            fn format(indent: usize, inline: bool) -> String {
                format!(
                    "[{}]",
                    vec![$($i::format(indent, inline)),*].join(", ")
                )
            }
        }
    };
    ( $i2:ident $(, $i:ident)* ) => {
        impl_tuples!(impl $i2 $(, $i)* );
        impl_tuples!($($i),*);
    };
    () => {};
}

macro_rules! impl_proxy {
    ($($t:tt)*) => {
        $($t)* {
            fn format(indent: usize, inline: bool) -> String { T::format(indent, inline) }
        }
    };
}

impl_primitives! {
    u8, i8, u16, i16, u32, i32, u64, i64, f32, f64 => "number",
    u128, i128 => "bigint",
    bool => "boolean",
    String, &str => "string"
}
impl_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_proxy!(impl<T: TS> TS for &T);
impl_proxy!(impl<T: TS> TS for Box<T>);
impl_proxy!(impl<T: TS> TS for std::sync::Arc<T>);
impl_proxy!(impl<T: TS> TS for std::rc::Rc<T>);
impl_proxy!(impl<'a, T: TS + ToOwned> TS for std::borrow::Cow<'a, T>);
impl_proxy!(impl<T: TS> TS for std::cell::Cell<T>);
impl_proxy!(impl<T: TS> TS for std::cell::RefCell<T>);

impl<T: TS> TS for Option<T> {
    fn decl() -> Option<String> {
        None
    }

    fn format(indent: usize, inline: bool) -> String {
        format!("{} | null", T::format(indent, inline))
    }
}

impl<T: TS> TS for Vec<T> {
    fn format(indent: usize, inline: bool) -> String {
        format!("{}[]", T::format(indent, inline))
    }
}