typescript_type_def/lib.rs
1//! Generate TypeScript type definitions for Rust types.
2//!
3//! This crate allows you to produce a TypeScript module containing type
4//! definitions which describe the JSON serialization of Rust types. The
5//! intended use is to define TypeScript types for data that is serialized from
6//! Rust types as JSON using [`serde_json`](https://docs.rs/serde_json/) so it
7//! can be safely used from TypeScript without needing to maintain a parallel
8//! set of type definitions.
9//!
10//! One example of where this crate is useful is when working on a web
11//! application project with a Rust backend and a TypeScript frontend. If the
12//! data used to communicate between the two is defined in Rust and uses
13//! [`serde_json`](https://docs.rs/serde_json/) to encode/decode it for
14//! transmission across the network, you can use this crate to automatically
15//! generate a TypeScript definition file for those types in order to use them
16//! safely in your frontend code. This process can even be completely automated
17//! if you use this crate in a
18//! [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html)
19//! for your server to write the definition file to your TypeScript source code
20//! directory.
21//!
22//! You can also use the Typescript type info generated by this library to write
23//! Typescript code that uses these type definitions. Rust types that implement
24//! [`TypeDef`] have an associated constant [`TypeDef::INFO`] which has a method
25//! [`TypeInfo::write_ref_expr`](type_expr::TypeInfo::write_ref_expr) that can
26//! be used for this purpose.
27//!
28//! # Features
29//!
30//! * `json_value` - Adds [`TypeDef`] impls for JSON value types from `serde_json`.
31//!
32//! # Examples
33//!
34//! Simple example:
35//! ```
36//! use serde::Serialize;
37//! use typescript_type_def::{
38//! write_definition_file,
39//! DefinitionFileOptions,
40//! TypeDef,
41//! };
42//!
43//! #[derive(Serialize, TypeDef)]
44//! struct Foo {
45//! a: usize,
46//! b: String,
47//! }
48//!
49//! let ts_module = {
50//! let mut buf = Vec::new();
51//! let options = DefinitionFileOptions::default();
52//! write_definition_file::<_, Foo>(&mut buf, options).unwrap();
53//! String::from_utf8(buf).unwrap()
54//! };
55//! assert_eq!(
56//! ts_module,
57//! r#"// AUTO-GENERATED by typescript-type-def
58//!
59//! export default types;
60//! export namespace types {
61//! export type Usize = number;
62//! export type Foo = {
63//! "a": types.Usize;
64//! "b": string;
65//! };
66//! }
67//! "#
68//! );
69//!
70//! let foo = Foo {
71//! a: 123,
72//! b: "hello".to_owned(),
73//! };
74//! let json = serde_json::to_string(&foo).unwrap();
75//! // This JSON matches the TypeScript type definition above
76//! assert_eq!(json, r#"{"a":123,"b":"hello"}"#);
77//! ```
78//!
79//! When working with a large codebase consisting of many types, a useful
80//! pattern is to declare an "API" type alias which lists all the types you want
81//! to make definitions for. For example:
82//! ```
83//! use serde::Serialize;
84//! use typescript_type_def::{write_definition_file, TypeDef};
85//!
86//! #[derive(Serialize, TypeDef)]
87//! struct Foo {
88//! a: String,
89//! }
90//!
91//! #[derive(Serialize, TypeDef)]
92//! struct Bar {
93//! a: String,
94//! }
95//!
96//! #[derive(Serialize, TypeDef)]
97//! struct Baz {
98//! a: Qux,
99//! }
100//!
101//! #[derive(Serialize, TypeDef)]
102//! struct Qux {
103//! a: String,
104//! }
105//!
106//! // This type lists all the top-level types we want to make definitions for.
107//! // You don't need to list *every* type in your API here, only ones that
108//! // wouldn't be referenced otherwise. Note that `Qux` is not mentioned, but
109//! // is still emitted because it is a dependency of `Baz`.
110//! type Api = (Foo, Bar, Baz);
111//!
112//! let ts_module = {
113//! let mut buf = Vec::new();
114//! write_definition_file::<_, Api>(&mut buf, Default::default()).unwrap();
115//! String::from_utf8(buf).unwrap()
116//! };
117//! assert_eq!(
118//! ts_module,
119//! r#"// AUTO-GENERATED by typescript-type-def
120//!
121//! export default types;
122//! export namespace types {
123//! export type Foo = {
124//! "a": string;
125//! };
126//! export type Bar = {
127//! "a": string;
128//! };
129//! export type Qux = {
130//! "a": string;
131//! };
132//! export type Baz = {
133//! "a": types.Qux;
134//! };
135//! }
136//! "#
137//! );
138//! ```
139//!
140//! Alternatively, you can use [`write_definition_file_from_type_infos`] with a list
141//! of [`TypeInfo`](type_expr::TypeInfo) references created at runtime to create
142//! a definition file. For example:
143//! ```
144//! use serde::Serialize;
145//! use typescript_type_def::{write_definition_file_from_type_infos, TypeDef};
146//!
147//! #[derive(Serialize, TypeDef)]
148//! struct Foo {
149//! a: String,
150//! }
151//!
152//! #[derive(Serialize, TypeDef)]
153//! struct Bar {
154//! a: String,
155//! }
156//!
157//! #[derive(Serialize, TypeDef)]
158//! struct Baz {
159//! a: Qux,
160//! }
161//!
162//! #[derive(Serialize, TypeDef)]
163//! struct Qux {
164//! a: String,
165//! }
166//!
167//! // This list contains type info for all the top-level types we want to make
168//! // definitions for.
169//! // You don't need to list *every* type in your API here, only ones that
170//! // wouldn't be referenced otherwise. Note that `Qux` is not mentioned, but
171//! // is still emitted because it is a dependency of `Baz`.
172//! let api = vec![
173//! &Foo::INFO,
174//! &Bar::INFO,
175//! &Baz::INFO,
176//! ];
177//!
178//! let ts_module = {
179//! let mut buf = Vec::new();
180//! write_definition_file_from_type_infos(
181//! &mut buf,
182//! Default::default(),
183//! &api,
184//! )
185//! .unwrap();
186//! String::from_utf8(buf).unwrap()
187//! };
188//! assert_eq!(
189//! ts_module,
190//! r#"// AUTO-GENERATED by typescript-type-def
191//!
192//! export default types;
193//! export namespace types {
194//! export type Foo = {
195//! "a": string;
196//! };
197//! export type Bar = {
198//! "a": string;
199//! };
200//! export type Qux = {
201//! "a": string;
202//! };
203//! export type Baz = {
204//! "a": types.Qux;
205//! };
206//! }
207//! "#
208//! );
209//! ```
210#![warn(rust_2018_idioms, clippy::all, missing_docs)]
211#![deny(clippy::correctness)]
212
213mod emit;
214mod impls;
215mod iter_def_deps;
216pub mod type_expr;
217
218pub use crate::emit::{
219 write_definition_file, write_definition_file_from_type_infos,
220 DefinitionFileOptions, Stats, TypeDef,
221};
222pub use crate::impls::Blob;
223
224/// A derive proc-macro for the [`TypeDef`] trait.
225///
226/// This macro can be used on structs and enums which also derive
227/// [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html)
228/// and/or
229/// [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html),
230/// and will generate a [`TypeDef`] implementation which matches the shape
231/// of the JSON produced by using [`serde_json`](https://docs.rs/serde_json/) on
232/// the target type. This macro will also read and adapt to `#[serde(...)]`
233/// attributes on the target type's definition.
234///
235/// This macro also reads the following attributes:
236/// * `#[type_def(namespace = "x.y.z")]` on the struct/enum body puts the
237/// TypeScript type definition under a namespace of `x.y.z`. Note that
238/// [`write_definition_file`] will additionally place all type
239/// definitions under a root namespace.
240/// * `#[type_def(type_of = "T")]` on a struct or tuple field will use the
241/// Rust type `T`'s TypeScript definition instead of the field's type.
242/// `T` must be a valid Rust type which implements [`TypeDef`] and whose
243/// JSON format matches the JSON format of the field's type. This
244/// attribute can be used to specify the type definition for a foreign
245/// type using your own type.
246///
247/// ## `serde` attribute support
248///
249/// Legend:
250/// * ✓ - full support
251/// * ? - may support in the future
252/// * ✗ - will not support
253///
254/// ### Container Attributes
255/// | Attribute | Support |
256/// |:-|:-:|
257/// | [`#[serde(rename = "name")]`](https://serde.rs/container-attrs.html#rename) | ✓ |
258/// | [`#[serde(rename_all = "...")]`](https://serde.rs/container-attrs.html#rename_all) | ✓ |
259/// | [`#[serde(rename_all_fields = "...")]`](https://serde.rs/container-attrs.html#rename_all_fields) | ✓ |
260/// | [`#[serde(deny_unknown_fields)]`](https://serde.rs/container-attrs.html#deny_unknown_fields) | ? |
261/// | [`#[serde(tag = "type")]`](https://serde.rs/container-attrs.html#tag) | ✓ |
262/// | [`#[serde(tag = "t", content = "c")]`](https://serde.rs/container-attrs.html#tag--content) | ✓ |
263/// | [`#[serde(untagged)]`](https://serde.rs/container-attrs.html#untagged) | ✓ |
264/// | [`#[serde(bound = "T: MyTrait")]`](https://serde.rs/container-attrs.html#bound) | ? |
265/// | [`#[serde(default)]`](https://serde.rs/container-attrs.html#default) | ? |
266/// | [`#[serde(default = "path")]`](https://serde.rs/container-attrs.html#default--path) | ? |
267/// | [`#[serde(remote = "...")]`](https://serde.rs/container-attrs.html#remote) | ✗ |
268/// | [`#[serde(transparent)]`](https://serde.rs/container-attrs.html#transparent) | ✓ |
269/// | [`#[serde(from = "FromType")]`](https://serde.rs/container-attrs.html#from) | ✗ |
270/// | [`#[serde(try_from = "FromType")]`](https://serde.rs/container-attrs.html#try_from) | ✗ |
271/// | [`#[serde(into = "IntoType")]`](https://serde.rs/container-attrs.html#into) | ✗ |
272/// | [`#[serde(crate = "...")]`](https://serde.rs/container-attrs.html#crate) | ✗ |
273///
274/// ### Variant Attributes
275/// | Attribute | Support |
276/// |:-|:-:|
277/// | [`#[serde(rename = "name")]`](https://serde.rs/variant-attrs.html#rename) | ✓ |
278/// | [`#[serde(alias = "name")]`](https://serde.rs/variant-attrs.html#alias) | ? |
279/// | [`#[serde(rename_all = "...")]`](https://serde.rs/variant-attrs.html#rename_all) | ✓ |
280/// | [`#[serde(skip)]`](https://serde.rs/variant-attrs.html#skip) | ✓ |
281/// | [`#[serde(skip_serializing)]`](https://serde.rs/variant-attrs.html#skip_serializing) | ✗ |
282/// | [`#[serde(skip_deserializing)]`](https://serde.rs/variant-attrs.html#skip_deserializing) | ✗ |
283/// | [`#[serde(serialize_with = "path")]`](https://serde.rs/variant-attrs.html#serialize_with) | ✗ |
284/// | [`#[serde(deserialize_with = "path")]`](https://serde.rs/variant-attrs.html#deserialize_with) | ✗ |
285/// | [`#[serde(with = "module")]`](https://serde.rs/variant-attrs.html#with) | ✗ |
286/// | [`#[serde(bound = "T: MyTrait")]`](https://serde.rs/variant-attrs.html#bound) | ? |
287/// | [`#[serde(borrow)]`](https://serde.rs/variant-attrs.html#borrow) | ? |
288/// | [`#[serde(borrow = "'a + 'b + ...")]`](https://serde.rs/variant-attrs.html#borrow) | ? |
289/// | [`#[serde(other)]`](https://serde.rs/variant-attrs.html#other) | ? |
290///
291/// ### Field Attributes
292/// | Attribute | Support |
293/// |:-|:-:|
294/// | [`#[serde(rename = "name")]`](https://serde.rs/field-attrs.html#rename) | ✓ |
295/// | [`#[serde(alias = "name")]`](https://serde.rs/field-attrs.html#alias) | ? |
296/// | [`#[serde(default)]`](https://serde.rs/field-attrs.html#default) | ✓ |
297/// | [`#[serde(default = "path")]`](https://serde.rs/field-attrs.html#default--path) | ✓ |
298/// | [`#[serde(flatten)]`](https://serde.rs/field-attrs.html#flatten) | ✓ |
299/// | [`#[serde(skip)]`](https://serde.rs/field-attrs.html#skip) | ✓ |
300/// | [`#[serde(skip_serializing)]`](https://serde.rs/field-attrs.html#skip_serializing) | ✗ |
301/// | [`#[serde(skip_deserializing)]`](https://serde.rs/field-attrs.html#skip_deserializing) | ✗ |
302/// | [`#[serde(skip_serializing_if = "path")]`](https://serde.rs/field-attrs.html#skip_serializing_if) | ✓ |
303/// | [`#[serde(serialize_with = "path")]`](https://serde.rs/field-attrs.html#serialize_with) | ✗ |
304/// | [`#[serde(deserialize_with = "path")]`](https://serde.rs/field-attrs.html#deserialize_with) | ✗ |
305/// | [`#[serde(with = "module")]`](https://serde.rs/field-attrs.html#with) | ✗ |
306/// | [`#[serde(borrow)]`](https://serde.rs/field-attrs.html#borrow) | ? |
307/// | [`#[serde(borrow = "'a + 'b + ...")]`](https://serde.rs/field-attrs.html#borrow) | ? |
308/// | [`#[serde(bound = "T: MyTrait")]`](https://serde.rs/field-attrs.html#bound) | ? |
309/// | [`#[serde(getter = "...")]`](https://serde.rs/field-attrs.html#getter) | ✗ |
310pub use typescript_type_def_derive::TypeDef;