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
//! [JSDoc](https://jsdoc.app) language exporter.
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
    html_logo_url = "https://github.com/oscartbeaumont/specta/raw/main/.github/logo-128.png",
    html_favicon_url = "https://github.com/oscartbeaumont/specta/raw/main/.github/logo-128.png"
)]

use std::{borrow::Cow, path::Path};

use specta::{Language, TypeMap};
use specta_typescript::{BigIntExportBehavior, CommentFormatterFn, FormatterFn};

// TODO: Ensure this is up to our `Typescript` exporters standards.

/// JSDoc language exporter.
#[derive(Debug, Clone, Default)]
pub struct JSDoc(pub specta_typescript::Typescript);

impl From<specta_typescript::Typescript> for JSDoc {
    fn from(ts: specta_typescript::Typescript) -> Self {
        Self(ts)
    }
}

impl JSDoc {
    /// Construct a new JSDoc exporter with the default options configured.
    pub fn new() -> Self {
        Default::default()
    }

    /// Configure a header for the file.
    ///
    /// This is perfect for configuring lint ignore rules or other file-level comments.
    pub fn header(mut self, header: impl Into<Cow<'static, str>>) -> Self {
        self.0.header = header.into();
        self
    }

    // TODO: Only keep this is TS stays responsible for exporting which it probs won't.
    /// Removes the default Specta header from the output.
    pub fn remove_default_header(mut self) -> Self {
        self.0.remove_default_header = true;
        self
    }

    /// Configure the BigInt handling behaviour
    pub fn bigint(mut self, bigint: BigIntExportBehavior) -> Self {
        self.0.bigint = bigint;
        self
    }

    /// Configure a function which is responsible for styling the comments to be exported
    ///
    /// Implementations:
    ///  - [`js_doc`](specta_typescript::lang::ts::js_doc)
    ///
    /// Not calling this method will default to the [`js_doc`](specta_typescript::lang::ts::js_doc) exporter.
    /// `None` will disable comment exporting.
    /// `Some(exporter)` will enable comment exporting using the provided exporter.
    pub fn comment_style(mut self, exporter: CommentFormatterFn) -> Self {
        self.0.comment_exporter = Some(exporter);
        self
    }

    /// Configure a function which is responsible for formatting the result file or files
    ///
    ///
    /// Built-in implementations:
    ///  - [`prettier`](specta_typescript:formatter:::prettier)
    ///  - [`ESLint`](specta_typescript::formatter::eslint)
    ///  - [`Biome`](specta_typescript::formatter::biome)e
    pub fn formatter(mut self, formatter: FormatterFn) -> Self {
        self.0.formatter = Some(formatter);
        self
    }
}

impl Language for JSDoc {
    type Error = specta_typescript::ExportError; // TODO: Custom error type

    // TODO: Make this properly export JSDoc
    fn export(&self, type_map: TypeMap) -> Result<String, Self::Error> {
        todo!("Coming soon...");
        // let mut out = self.0.header.to_string();
        // if !self.0.remove_default_header {
        //     out += "// This file has been generated by Specta. DO NOT EDIT.\n\n";
        // }

        // if let Some((ty_name, l0, l1)) = detect_duplicate_type_names(&type_map).into_iter().next() {
        //     return Err(ExportError::DuplicateTypeName(ty_name, l0, l1));
        // }

        // for (_, ty) in type_map.iter() {
        //     is_valid_ty(&ty.inner, &type_map)?;

        //     out += &export_named_datatype(&self.0, ty, &type_map)?;
        //     out += "\n\n";
        // }

        // Ok(out)
    }

    fn format(&self, path: &Path) -> Result<(), Self::Error> {
        if let Some(formatter) = self.0.formatter {
            formatter(path)?;
        }
        Ok(())
    }
}