1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use use_js_module::JsModuleSpecifier;
5
6#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub enum JsExportKind {
9 Default,
10 Named,
11 Namespace,
12 TypeOnly,
13}
14
15#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct JsExportSpecifier {
18 local: Option<String>,
19 exported: Option<String>,
20}
21
22impl JsExportSpecifier {
23 #[must_use]
25 pub fn new(local: Option<&str>, exported: Option<&str>) -> Self {
26 Self {
27 local: clean_optional(local),
28 exported: clean_optional(exported),
29 }
30 }
31
32 #[must_use]
34 pub fn named(local: &str, exported: Option<&str>) -> Self {
35 Self::new(Some(local), exported)
36 }
37
38 #[must_use]
40 pub fn default(local: &str) -> Self {
41 Self::new(Some(local), Some("default"))
42 }
43
44 #[must_use]
46 pub fn local(&self) -> Option<&str> {
47 self.local.as_deref()
48 }
49
50 #[must_use]
52 pub fn exported(&self) -> Option<&str> {
53 self.exported.as_deref()
54 }
55}
56
57#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct JsExportStatementParts {
60 kind: JsExportKind,
61 source: Option<JsModuleSpecifier>,
62 specifiers: Vec<JsExportSpecifier>,
63}
64
65impl JsExportStatementParts {
66 #[must_use]
68 pub const fn new(kind: JsExportKind) -> Self {
69 Self {
70 kind,
71 source: None,
72 specifiers: Vec::new(),
73 }
74 }
75
76 #[must_use]
78 pub fn with_source(mut self, source: JsModuleSpecifier) -> Self {
79 self.source = Some(source);
80 self
81 }
82
83 #[must_use]
85 pub fn with_specifier(mut self, specifier: JsExportSpecifier) -> Self {
86 self.specifiers.push(specifier);
87 self
88 }
89
90 #[must_use]
92 pub const fn kind(&self) -> JsExportKind {
93 self.kind
94 }
95
96 #[must_use]
98 pub const fn source(&self) -> Option<&JsModuleSpecifier> {
99 self.source.as_ref()
100 }
101
102 #[must_use]
104 pub fn specifiers(&self) -> &[JsExportSpecifier] {
105 &self.specifiers
106 }
107}
108
109fn clean_optional(value: Option<&str>) -> Option<String> {
110 value.and_then(|text| {
111 let trimmed = text.trim();
112 (!trimmed.is_empty()).then(|| trimmed.to_string())
113 })
114}
115
116#[cfg(test)]
117mod tests {
118 use super::{JsExportKind, JsExportSpecifier, JsExportStatementParts};
119 use use_js_module::{JsModuleSpecifier, JsModuleSpecifierError};
120
121 #[test]
122 fn models_re_export_metadata() -> Result<(), JsModuleSpecifierError> {
123 let source = JsModuleSpecifier::new("./button.js")?;
124 let parts = JsExportStatementParts::new(JsExportKind::Named)
125 .with_source(source)
126 .with_specifier(JsExportSpecifier::named("Button", Some("Button")));
127
128 assert_eq!(parts.kind(), JsExportKind::Named);
129 assert_eq!(
130 parts.source().map(JsModuleSpecifier::as_str),
131 Some("./button.js")
132 );
133 assert_eq!(parts.specifiers()[0].local(), Some("Button"));
134 Ok(())
135 }
136}