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
//! Generates TypeScript code from a Move IDL.

pub mod format;
pub mod idl_module;
pub mod idl_package;
pub mod idl_struct;
pub mod idl_type;
pub mod script_function;

use crate::format::indent;
use anyhow::*;
use format::gen_doc_string;
use idl_module::IDLModuleGenerator;
use move_idl::{IDLModule, IDLPackage};
use serde::Serialize;
use std::fmt::Display;

/// Generate TypeScript code for a value.
pub trait Codegen {
    fn generate_typescript(&self, ctx: &CodegenContext) -> Result<String>;
}

#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CodeText(String);

impl Codegen for CodeText {
    fn generate_typescript(&self, _ctx: &CodegenContext) -> Result<String> {
        Ok(self.to_string())
    }
}

impl AsRef<[u8]> for CodeText {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl Display for CodeText {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl CodeText {
    pub fn new(s: &str) -> Self {
        CodeText(s.to_string())
    }

    pub fn new_reexport(name: &str, path: &str) -> Self {
        format!("export * as {} from \"{}\";", name, path).into()
    }

    pub fn new_named_reexport(name: &str, path: &str) -> Self {
        format!("export {{ {} }} from \"{}\";", name, path).into()
    }

    pub fn new_fields_export(name: &str, fields: &str) -> Self {
        format!("export type {} = {{\n{} }};", name, fields).into()
    }

    pub fn append_newline(&self) -> Self {
        CodeText(format!("{}\n", self.0))
    }

    /// Creates a `export const {name} = {value} as const` statement.
    pub fn new_const_export<T>(name: &str, value: &T) -> Result<Self>
    where
        T: ?Sized + Serialize,
    {
        Ok(format!(
            "export const {} = {} as const;",
            name,
            serde_json::to_string_pretty(value)?
        )
        .into())
    }

    pub fn try_join_with_separator<'a, I>(values: I, separator: &str) -> Result<CodeText>
    where
        I: IntoIterator<Item = &'a CodeText>,
    {
        Ok(values
            .into_iter()
            .map(|v| v.to_string())
            .filter(|s| !s.is_empty())
            .collect::<Vec<_>>()
            .join(separator)
            .into())
    }

    pub fn docs(&self, docs: &str) -> CodeText {
        format!("{}{}", gen_doc_string(docs), self).into()
    }

    pub fn module_docs(&self, docs: &str) -> CodeText {
        format!(
            "{}\n{}",
            gen_doc_string(&format!("{}\n\n@module", docs)),
            self
        )
        .into()
    }
}

pub struct CodegenContext<'info> {
    pkg: &'info IDLPackage,
}

impl<'info> CodegenContext<'info> {
    pub fn new(pkg: &'info IDLPackage) -> Self {
        CodegenContext { pkg }
    }

    pub fn get_module_generator(&self, value: &'info IDLModule) -> IDLModuleGenerator<'info> {
        IDLModuleGenerator::new(value)
    }

    pub fn generate<T: Codegen>(&self, value: &T) -> Result<CodeText> {
        Ok(CodeText(value.generate_typescript(self)?))
    }

    pub fn try_join_with_separator<'a, I, T>(&self, values: I, separator: &str) -> Result<CodeText>
    where
        I: IntoIterator<Item = &'a T>,
        T: Codegen + 'a,
    {
        CodeText::try_join_with_separator(
            &values
                .into_iter()
                .map(|v| self.generate(v))
                .collect::<Result<Vec<_>>>()?,
            separator,
        )
    }

    pub fn try_join<'a, I, T>(&self, values: I) -> Result<CodeText>
    where
        I: IntoIterator<Item = &'a T>,
        T: Codegen + 'a,
    {
        self.try_join_with_separator(values, "\n\n")
    }
}

impl From<&str> for CodeText {
    fn from(s: &str) -> Self {
        s.to_string().into()
    }
}

impl From<String> for CodeText {
    fn from(s: String) -> Self {
        CodeText(s)
    }
}

impl From<CodeText> for String {
    fn from(s: CodeText) -> Self {
        s.0
    }
}

impl CodeText {
    pub fn indent(&self) -> CodeText {
        indent(&self.0).into()
    }

    pub fn trim(&self) -> CodeText {
        self.0.trim().into()
    }
}