specta_typescript/
js_doc.rs

1use std::borrow::Borrow;
2
3use specta::{
4    datatype::{DeprecatedType, GenericType},
5    TypeCollection,
6};
7use typescript::CommentFormatterArgs;
8
9use super::*;
10
11pub fn typedef_named_datatype(
12    cfg: &Typescript,
13    typ: &NamedDataType,
14    type_map: &TypeCollection,
15) -> Output {
16    typedef_named_datatype_inner(
17        &ExportContext {
18            cfg,
19            path: vec![],
20            // TODO: Should JS doc support per field or variant comments???
21            is_export: false,
22        },
23        typ,
24        type_map,
25    )
26}
27
28fn typedef_named_datatype_inner(
29    ctx: &ExportContext,
30    typ: &NamedDataType,
31    type_map: &TypeCollection,
32) -> Output {
33    let name = typ.name();
34    let docs = typ.docs();
35    let deprecated = typ.deprecated();
36    let item = &typ.inner;
37
38    let ctx = ctx.with(PathItem::Type(name.clone()));
39
40    let name = sanitise_type_name(ctx.clone(), NamedLocation::Type, name)?;
41
42    let mut inline_ts = String::new();
43    datatype_inner(
44        ctx.clone(),
45        &FunctionResultVariant::Value(typ.inner.clone()),
46        type_map,
47        &mut inline_ts,
48    )?;
49
50    let mut builder = super::comments::js_doc_builder(CommentFormatterArgs { docs, deprecated });
51
52    item.generics()
53        .into_iter()
54        .flatten()
55        .for_each(|generic| builder.push_generic(generic));
56
57    builder.push_internal(["@typedef { ", &inline_ts, " } ", &name]);
58
59    Ok(builder.build())
60}
61
62const START: &str = "/**\n";
63
64pub struct Builder {
65    value: String,
66}
67
68impl Builder {
69    pub fn push(&mut self, line: &str) {
70        self.push_internal([line.trim()]);
71    }
72
73    pub(crate) fn push_internal<'a>(&mut self, parts: impl IntoIterator<Item = &'a str>) {
74        self.value.push_str(" * ");
75
76        for part in parts.into_iter() {
77            self.value.push_str(part);
78        }
79
80        self.value.push('\n');
81    }
82
83    pub fn push_deprecated(&mut self, typ: &DeprecatedType) {
84        self.push_internal(
85            ["@deprecated"].into_iter().chain(
86                match typ {
87                    DeprecatedType::DeprecatedWithSince {
88                        note: message,
89                        since,
90                    } => Some((since.as_ref(), message)),
91                    _ => None,
92                }
93                .map(|(since, message)| {
94                    [" ", message.trim()].into_iter().chain(
95                        since
96                            .map(|since| [" since ", since.trim()])
97                            .into_iter()
98                            .flatten(),
99                    )
100                })
101                .into_iter()
102                .flatten(),
103            ),
104        );
105    }
106
107    pub fn push_generic(&mut self, generic: &GenericType) {
108        self.push_internal(["@template ", generic.borrow()])
109    }
110
111    pub fn build(mut self) -> String {
112        if self.value == START {
113            return String::new();
114        }
115
116        self.value.push_str(" */\n");
117        self.value
118    }
119}
120
121impl Default for Builder {
122    fn default() -> Self {
123        Self {
124            value: START.to_string(),
125        }
126    }
127}
128
129impl<T: AsRef<str>> Extend<T> for Builder {
130    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
131        for item in iter {
132            self.push(item.as_ref());
133        }
134    }
135}