wit_encoder/
world.rs

1use std::fmt;
2
3use crate::{Docs, Include, Interface, Render, RenderOpts, StandaloneFunc, Use, ident::Ident};
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
8pub struct World {
9    /// The WIT identifier name of this world.
10    name: Ident,
11
12    /// Interface uses
13    uses: Vec<Use>,
14
15    /// All imported and exported items into this world.
16    items: Vec<WorldItem>,
17
18    /// Documentation associated with this world declaration.
19    docs: Option<Docs>,
20}
21
22impl World {
23    /// Create a new world.
24    pub fn new(name: impl Into<Ident>) -> Self {
25        Self {
26            name: name.into(),
27            uses: vec![],
28            items: vec![],
29            docs: None,
30        }
31    }
32
33    /// Add a `name` to the world
34    pub fn set_name(&mut self, name: impl Into<Ident>) {
35        self.name = name.into();
36    }
37
38    pub fn name(&self) -> &Ident {
39        &self.name
40    }
41
42    pub fn items(&self) -> &[WorldItem] {
43        &self.items
44    }
45
46    pub fn items_mut(&mut self) -> &mut Vec<WorldItem> {
47        &mut self.items
48    }
49
50    /// Add an import or export to the world
51    pub fn item(&mut self, item: WorldItem) {
52        self.items.push(item);
53    }
54
55    pub fn inline_interface_import(&mut self, value: Interface) {
56        self.item(WorldItem::inline_interface_import(value));
57    }
58    pub fn inline_interface_export(&mut self, value: Interface) {
59        self.item(WorldItem::inline_interface_export(value));
60    }
61    pub fn named_interface_import(&mut self, value: impl Into<WorldNamedInterface>) {
62        self.item(WorldItem::named_interface_import(value));
63    }
64    pub fn named_interface_export(&mut self, value: impl Into<WorldNamedInterface>) {
65        self.item(WorldItem::named_interface_export(value));
66    }
67    pub fn function_import(&mut self, value: StandaloneFunc) {
68        self.item(WorldItem::function_import(value));
69    }
70    pub fn function_export(&mut self, value: StandaloneFunc) {
71        self.item(WorldItem::function_export(value));
72    }
73    pub fn include(&mut self, include: Include) {
74        self.item(WorldItem::Include(include));
75    }
76
77    pub fn uses(&self) -> &[Use] {
78        &self.uses
79    }
80    pub fn uses_mut(&mut self) -> &mut [Use] {
81        &mut self.uses
82    }
83    pub fn use_(&mut self, use_: Use) {
84        self.uses.push(use_);
85    }
86    pub fn use_type(
87        &mut self,
88        target: impl Into<Ident>,
89        item: impl Into<Ident>,
90        rename: Option<Ident>,
91    ) {
92        let target = target.into();
93        let use_ = self.uses.iter_mut().find(|u| u.target() == &target);
94        match use_ {
95            Some(use_) => use_.item(item, rename),
96            None => {
97                self.use_({
98                    let mut use_ = Use::new(target);
99                    use_.item(item, rename);
100                    use_
101                });
102            }
103        }
104    }
105
106    pub fn docs(&self) -> Option<&Docs> {
107        self.docs.as_ref()
108    }
109
110    /// Set the documentation
111    pub fn set_docs(&mut self, docs: Option<impl Into<Docs>>) {
112        self.docs = docs.map(|d| d.into());
113    }
114}
115
116impl Render for World {
117    fn render(&self, f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
118        fn import(f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
119            write!(f, "{}import ", opts.spaces())
120        }
121        fn export(f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
122            write!(f, "{}export ", opts.spaces())
123        }
124        fn render_function(
125            f: &mut fmt::Formatter<'_>,
126            _opts: &RenderOpts,
127            func: &StandaloneFunc,
128        ) -> fmt::Result {
129            let opt_async = if func.async_ { "async " } else { "" };
130            write!(f, "{}: {opt_async}func({})", func.name, func.params)?;
131            if let Some(ty) = &func.result {
132                write!(f, " -> {ty}")?;
133            }
134            write!(f, ";\n")?;
135            Ok(())
136        }
137        write!(f, "{}world {} {{\n", opts.spaces(), self.name)?;
138        let opts = &opts.indent();
139        self.uses.render(f, opts)?;
140        for item in &self.items {
141            match item {
142                WorldItem::InlineInterfaceImport(interface) => {
143                    if let Some(docs) = &interface.docs {
144                        docs.render(f, opts)?;
145                    }
146                    import(f, opts)?;
147                    write!(f, "{}: interface {{", interface.name)?;
148                    if !interface.uses.is_empty() || !interface.items.is_empty() {
149                        write!(f, "\n")?;
150                        interface.uses.render(f, &opts.indent())?;
151                        interface.items.render(f, &opts.indent())?;
152                        write!(f, "{}}}\n", opts.spaces())?;
153                    } else {
154                        write!(f, "}}\n")?;
155                    }
156                }
157                WorldItem::InlineInterfaceExport(interface) => {
158                    if let Some(docs) = &interface.docs {
159                        docs.render(f, opts)?;
160                    }
161                    export(f, opts)?;
162                    write!(f, "{}: interface {{", interface.name)?;
163                    if !interface.items.is_empty() {
164                        write!(f, "\n")?;
165                        interface.items.render(f, &opts.indent())?;
166                        write!(f, "{}}}\n", opts.spaces())?;
167                    } else {
168                        write!(f, "}}\n")?;
169                    }
170                }
171                WorldItem::NamedInterfaceImport(interface) => {
172                    if let Some(docs) = &interface.docs {
173                        docs.render(f, opts)?;
174                    }
175                    import(f, opts)?;
176                    write!(f, "{};\n", interface.name)?;
177                }
178                WorldItem::NamedInterfaceExport(interface) => {
179                    if let Some(docs) = &interface.docs {
180                        docs.render(f, opts)?;
181                    }
182                    export(f, opts)?;
183                    write!(f, "{};\n", interface.name)?;
184                }
185                WorldItem::FunctionImport(function) => {
186                    if let Some(docs) = &function.docs {
187                        docs.render(f, opts)?;
188                    }
189                    import(f, opts)?;
190                    render_function(f, opts, function)?;
191                }
192                WorldItem::FunctionExport(function) => {
193                    if let Some(docs) = &function.docs {
194                        docs.render(f, opts)?;
195                    }
196                    export(f, opts)?;
197                    render_function(f, opts, function)?;
198                }
199                WorldItem::Include(include) => include.render(f, opts)?,
200            }
201        }
202        let opts = &opts.outdent();
203        write!(f, "{}}}\n", opts.spaces())?;
204        Ok(())
205    }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
211pub enum WorldItem {
212    /// An imported inline interface
213    InlineInterfaceImport(Interface),
214
215    /// An exported inline interface
216    InlineInterfaceExport(Interface),
217
218    /// Refers to a named interface import
219    NamedInterfaceImport(WorldNamedInterface),
220
221    /// Refers to a named interface export
222    NamedInterfaceExport(WorldNamedInterface),
223
224    /// A function is being directly imported from this world.
225    FunctionImport(StandaloneFunc),
226
227    /// A function is being directly exported from this world.
228    FunctionExport(StandaloneFunc),
229
230    /// Include type
231    Include(Include),
232}
233
234impl WorldItem {
235    pub fn inline_interface_import(value: Interface) -> Self {
236        Self::InlineInterfaceImport(value)
237    }
238    pub fn inline_interface_export(value: Interface) -> Self {
239        Self::InlineInterfaceExport(value)
240    }
241    pub fn named_interface_import(value: impl Into<WorldNamedInterface>) -> Self {
242        Self::NamedInterfaceImport(value.into())
243    }
244    pub fn named_interface_export(value: impl Into<WorldNamedInterface>) -> Self {
245        Self::NamedInterfaceExport(value.into())
246    }
247    pub fn function_import(value: StandaloneFunc) -> Self {
248        Self::FunctionImport(value)
249    }
250    pub fn function_export(value: StandaloneFunc) -> Self {
251        Self::FunctionExport(value)
252    }
253    pub fn include(value: impl Into<Ident>) -> Self {
254        Self::Include(Include::new(value))
255    }
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
259#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
260#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
261pub struct WorldNamedInterface {
262    /// Name of this interface.
263    pub(crate) name: Ident,
264
265    /// Documentation associated with this interface.
266    pub(crate) docs: Option<Docs>,
267}
268
269impl<N> From<N> for WorldNamedInterface
270where
271    N: Into<Ident>,
272{
273    fn from(name: N) -> Self {
274        Self::new(name)
275    }
276}
277
278impl WorldNamedInterface {
279    pub fn new(name: impl Into<Ident>) -> Self {
280        Self {
281            name: name.into(),
282            docs: None,
283        }
284    }
285
286    pub fn set_name(&mut self, name: impl Into<Ident>) {
287        self.name = name.into();
288    }
289
290    pub fn name(&self) -> &Ident {
291        &self.name
292    }
293
294    pub fn set_docs(&mut self, docs: Option<impl Into<Docs>>) {
295        self.docs = docs.map(|d| d.into());
296    }
297
298    pub fn docs(&self) -> Option<&Docs> {
299        self.docs.as_ref()
300    }
301}