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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
//! Generate TypeScript type definitions for Rust types.
//!
//! This crate allows you to produce a TypeScript module containing type
//! definitions which describe the JSON serialization of Rust types. The
//! intended use is to define TypeScript types for data that is serialized from
//! Rust types as JSON using [`serde_json`](https://docs.rs/serde_json/) so it
//! can be safely used from TypeScript without needing to maintain a parallel
//! set of type definitions.
//!
//! One example of where this crate is useful is when working on a web
//! application project with a Rust backend and a TypeScript frontend. If the
//! data used to communicate between the two is defined in Rust and uses
//! [`serde_json`](https://docs.rs/serde_json/) to encode/decode it for
//! transmission across the network, you can use this crate to automatically
//! generate a TypeScript definition file for those types in order to use them
//! safely in your frontend code. This process can even be completely automated
//! if you use this crate in a
//! [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html)
//! for your server to write the definition file to your TypeScript source code
//! directory.
//!
//! # Examples
//!
//! Simple example:
//! ```
//! use serde::Serialize;
//! use typescript_type_def::{
//! write_definition_file,
//! DefinitionFileOptions,
//! TypeDef,
//! };
//!
//! #[derive(Serialize, TypeDef)]
//! struct Foo {
//! a: usize,
//! b: String,
//! }
//!
//! let ts_module = {
//! let mut buf = Vec::new();
//! let options = DefinitionFileOptions::default();
//! write_definition_file::<_, Foo>(&mut buf, options).unwrap();
//! String::from_utf8(buf).unwrap()
//! };
//! assert_eq!(
//! ts_module,
//! r#"// AUTO-GENERATED by typescript-type-def
//!
//! export default types;
//! export namespace types{
//! export type Usize=number;
//! export type Foo={"a":types.Usize;"b":string;};
//! }
//! "#
//! );
//!
//! let foo = Foo {
//! a: 123,
//! b: "hello".to_owned(),
//! };
//! let json = serde_json::to_string(&foo).unwrap();
//! // This JSON matches the TypeScript type definition above
//! assert_eq!(json, r#"{"a":123,"b":"hello"}"#);
//! ```
//!
//! When working with a large codebase consisting of many types, a useful
//! pattern is to declare an "API" type alias which lists all the types you want
//! to make definitions for. For example:
//! ```
//! use serde::Serialize;
//! use typescript_type_def::{write_definition_file, TypeDef};
//!
//! #[derive(Serialize, TypeDef)]
//! struct Foo {
//! a: String,
//! }
//!
//! #[derive(Serialize, TypeDef)]
//! struct Bar {
//! a: String,
//! }
//!
//! #[derive(Serialize, TypeDef)]
//! struct Baz {
//! a: Qux,
//! }
//!
//! #[derive(Serialize, TypeDef)]
//! struct Qux {
//! a: String,
//! }
//!
//! // This type lists all the top-level types we want to make definitions for.
//! // You don't need to list *every* type in your API here, only ones that
//! // wouldn't be referenced otherwise. Note that `Qux` is not mentioned, but
//! // is still emitted because it is a dependency of `Baz`.
//! type Api = (Foo, Bar, Baz);
//!
//! let ts_module = {
//! let mut buf = Vec::new();
//! write_definition_file::<_, Api>(&mut buf, Default::default()).unwrap();
//! String::from_utf8(buf).unwrap()
//! };
//! assert_eq!(
//! ts_module,
//! r#"// AUTO-GENERATED by typescript-type-def
//!
//! export default types;
//! export namespace types{
//! export type Foo={"a":string;};
//! export type Bar={"a":string;};
//! export type Qux={"a":string;};
//! export type Baz={"a":types.Qux;};
//! }
//! "#
//! );
//! ```
#![warn(rust_2018_idioms, clippy::all, missing_docs)]
#![deny(clippy::correctness)]
mod emit;
mod impls;
mod iter_def_deps;
pub mod type_expr;
pub use crate::emit::{
write_definition_file,
DefinitionFileOptions,
Stats,
TypeDef,
};
/// A derive proc-macro for the [`TypeDef`] trait.
///
/// This macro can be used on structs and enums which also derive
/// [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html)
/// and/or
/// [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html),
/// and will generate a [`TypeDef`] implementation which matches the shape
/// of the JSON produced by using [`serde_json`](https://docs.rs/serde_json/) on
/// the target type. This macro will also read and adapt to `#[serde(...)]`
/// attributes on the target type's definition.
///
/// This macro also reads the following attributes:
/// * `#[type_def(namespace = "x.y.z")]` on the struct/enum body puts the
/// TypeScript type definition under a namespace of `x.y.z`. Note that
/// [`write_definition_file`] will additionally place all type
/// definitions under a root namespace.
/// * `#[type_def(type_of = "T")]` on a struct or tuple field will use the
/// Rust type `T`'s TypeScript definition instead of the field's type.
/// `T` must be a valid Rust type which implements [`TypeDef`] and whose
/// JSON format matches the JSON format of the field's type. This
/// attribute can be used to specify the type definition for a foreign
/// type using your own type.
///
/// ## `serde` attribute support
///
/// Legend:
/// * ✓ - full support
/// * ? - may support in the future
/// * ✗ - will not support
///
/// ### Container Attributes
/// | Attribute | Support |
/// |:-|:-:|
/// | [`#[serde(rename = "name")]`](https://serde.rs/container-attrs.html#rename) | ✓ |
/// | [`#[serde(rename_all = "...")]`](https://serde.rs/container-attrs.html#rename_all) | ✓ |
/// | [`#[serde(deny_unknown_fields)]`](https://serde.rs/container-attrs.html#deny_unknown_fields) | ? |
/// | [`#[serde(tag = "type")]`](https://serde.rs/container-attrs.html#tag) | ✓ |
/// | [`#[serde(tag = "t", content = "c")]`](https://serde.rs/container-attrs.html#tag--content) | ✓ |
/// | [`#[serde(untagged)]`](https://serde.rs/container-attrs.html#untagged) | ✓ |
/// | [`#[serde(bound = "T: MyTrait")]`](https://serde.rs/container-attrs.html#bound) | ? |
/// | [`#[serde(default)]`](https://serde.rs/container-attrs.html#default) | ? |
/// | [`#[serde(default = "path")]`](https://serde.rs/container-attrs.html#default--path) | ? |
/// | [`#[serde(remote = "...")]`](https://serde.rs/container-attrs.html#remote) | ✗ |
/// | [`#[serde(transparent)]`](https://serde.rs/container-attrs.html#transparent) | ✓ |
/// | [`#[serde(from = "FromType")]`](https://serde.rs/container-attrs.html#from) | ✗ |
/// | [`#[serde(try_from = "FromType")]`](https://serde.rs/container-attrs.html#try_from) | ✗ |
/// | [`#[serde(into = "IntoType")]`](https://serde.rs/container-attrs.html#into) | ✗ |
/// | [`#[serde(crate = "...")]`](https://serde.rs/container-attrs.html#crate) | ✗ |
///
/// ### Variant Attributes
/// | Attribute | Support |
/// |:-|:-:|
/// | [`#[serde(rename = "name")]`](https://serde.rs/variant-attrs.html#rename) | ✓ |
/// | [`#[serde(alias = "name")]`](https://serde.rs/variant-attrs.html#alias) | ? |
/// | [`#[serde(rename_all = "...")]`](https://serde.rs/variant-attrs.html#rename_all) | ✓ |
/// | [`#[serde(skip)]`](https://serde.rs/variant-attrs.html#skip) | ✓ |
/// | [`#[serde(skip_serializing)]`](https://serde.rs/variant-attrs.html#skip_serializing) | ✗ |
/// | [`#[serde(skip_deserializing)]`](https://serde.rs/variant-attrs.html#skip_deserializing) | ✗ |
/// | [`#[serde(serialize_with = "path")]`](https://serde.rs/variant-attrs.html#serialize_with) | ✗ |
/// | [`#[serde(deserialize_with = "path")]`](https://serde.rs/variant-attrs.html#deserialize_with) | ✗ |
/// | [`#[serde(with = "module")]`](https://serde.rs/variant-attrs.html#with) | ✗ |
/// | [`#[serde(bound = "T: MyTrait")]`](https://serde.rs/variant-attrs.html#bound) | ? |
/// | [`#[serde(borrow)]`](https://serde.rs/variant-attrs.html#borrow) | ? |
/// | [`#[serde(borrow = "'a + 'b + ...")]`](https://serde.rs/variant-attrs.html#borrow) | ? |
/// | [`#[serde(other)]`](https://serde.rs/variant-attrs.html#other) | ? |
///
/// ### Field Attributes
/// | Attribute | Support |
/// |:-|:-:|
/// | [`#[serde(rename = "name")]`](https://serde.rs/field-attrs.html#rename) | ✓ |
/// | [`#[serde(alias = "name")]`](https://serde.rs/field-attrs.html#alias) | ? |
/// | [`#[serde(default)]`](https://serde.rs/field-attrs.html#default) | ✓ |
/// | [`#[serde(default = "path")]`](https://serde.rs/field-attrs.html#default--path) | ✓ |
/// | [`#[serde(flatten)]`](https://serde.rs/field-attrs.html#flatten) | ✓ |
/// | [`#[serde(skip)]`](https://serde.rs/field-attrs.html#skip) | ✓ |
/// | [`#[serde(skip_serializing)]`](https://serde.rs/field-attrs.html#skip_serializing) | ✗ |
/// | [`#[serde(skip_deserializing)]`](https://serde.rs/field-attrs.html#skip_deserializing) | ✗ |
/// | [`#[serde(skip_serializing_if = "path")]`](https://serde.rs/field-attrs.html#skip_serializing_if) | ✓ |
/// | [`#[serde(serialize_with = "path")]`](https://serde.rs/field-attrs.html#serialize_with) | ✗ |
/// | [`#[serde(deserialize_with = "path")]`](https://serde.rs/field-attrs.html#deserialize_with) | ✗ |
/// | [`#[serde(with = "module")]`](https://serde.rs/field-attrs.html#with) | ✗ |
/// | [`#[serde(borrow)]`](https://serde.rs/field-attrs.html#borrow) | ? |
/// | [`#[serde(borrow = "'a + 'b + ...")]`](https://serde.rs/field-attrs.html#borrow) | ? |
/// | [`#[serde(bound = "T: MyTrait")]`](https://serde.rs/field-attrs.html#bound) | ? |
/// | [`#[serde(getter = "...")]`](https://serde.rs/field-attrs.html#getter) | ✗ |
pub use typescript_type_def_derive::TypeDef;