baobao_codegen_typescript/ast/
arrays.rs

1//! TypeScript/JavaScript array literal builder.
2
3/// An element in a JavaScript array literal.
4#[derive(Debug, Clone)]
5pub enum ArrayElement {
6    /// A literal string value (will be quoted).
7    String(String),
8    /// A raw expression (will not be quoted).
9    Raw(String),
10}
11
12/// Builder for JavaScript/TypeScript array literals.
13///
14/// Supports the `as const` TypeScript assertion for literal types.
15#[derive(Debug, Clone, Default)]
16pub struct JsArray {
17    elements: Vec<ArrayElement>,
18    as_const: bool,
19}
20
21impl JsArray {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    /// Create an array from string values (will be quoted).
27    pub fn from_strings<I, S>(iter: I) -> Self
28    where
29        I: IntoIterator<Item = S>,
30        S: Into<String>,
31    {
32        Self {
33            elements: iter
34                .into_iter()
35                .map(|s| ArrayElement::String(s.into()))
36                .collect(),
37            as_const: false,
38        }
39    }
40
41    /// Add a string element (will be quoted).
42    pub fn string(mut self, value: impl Into<String>) -> Self {
43        self.elements.push(ArrayElement::String(value.into()));
44        self
45    }
46
47    /// Add a raw expression element (will not be quoted).
48    pub fn raw(mut self, value: impl Into<String>) -> Self {
49        self.elements.push(ArrayElement::Raw(value.into()));
50        self
51    }
52
53    /// Add the `as const` TypeScript assertion.
54    pub fn as_const(mut self) -> Self {
55        self.as_const = true;
56        self
57    }
58
59    /// Check if the array is empty.
60    pub fn is_empty(&self) -> bool {
61        self.elements.is_empty()
62    }
63
64    /// Build the array literal as a string.
65    pub fn build(&self) -> String {
66        let elements_str = self
67            .elements
68            .iter()
69            .map(|e| match e {
70                ArrayElement::String(s) => format!("\"{}\"", s),
71                ArrayElement::Raw(s) => s.clone(),
72            })
73            .collect::<Vec<_>>()
74            .join(", ");
75
76        if self.as_const {
77            format!("[{}] as const", elements_str)
78        } else {
79            format!("[{}]", elements_str)
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_empty_array() {
90        let arr = JsArray::new().build();
91        assert_eq!(arr, "[]");
92    }
93
94    #[test]
95    fn test_string_array() {
96        let arr = JsArray::new().string("foo").string("bar").build();
97        assert_eq!(arr, "[\"foo\", \"bar\"]");
98    }
99
100    #[test]
101    fn test_raw_array() {
102        let arr = JsArray::new().raw("42").raw("true").build();
103        assert_eq!(arr, "[42, true]");
104    }
105
106    #[test]
107    fn test_as_const() {
108        let arr = JsArray::new().string("a").string("b").as_const().build();
109        assert_eq!(arr, "[\"a\", \"b\"] as const");
110    }
111
112    #[test]
113    fn test_from_strings() {
114        let choices = vec!["dev", "prod", "test"];
115        let arr = JsArray::from_strings(choices).as_const().build();
116        assert_eq!(arr, "[\"dev\", \"prod\", \"test\"] as const");
117    }
118
119    #[test]
120    fn test_mixed_array() {
121        let arr = JsArray::new()
122            .string("name")
123            .raw("123")
124            .string("value")
125            .build();
126        assert_eq!(arr, "[\"name\", 123, \"value\"]");
127    }
128}