wit_encoder/
package.rs

1use std::fmt;
2use std::ops::{Deref, DerefMut};
3
4use semver::Version;
5
6use crate::{Interface, Render, RenderOpts, World, ident::Ident};
7
8/// A WIT package.
9///
10/// A package is a collection of interfaces and worlds. Packages additionally
11/// have a unique identifier that affects generated components and uniquely
12/// identifiers this particular package.
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
16pub struct Package {
17    /// A unique name corresponding to this package.
18    name: PackageName,
19
20    /// World items
21    items: Vec<PackageItem>,
22}
23
24impl Package {
25    /// Create a new instance of `Package`.
26    pub fn new(name: PackageName) -> Self {
27        Self {
28            name,
29            items: vec![],
30        }
31    }
32
33    pub fn name(&self) -> &PackageName {
34        &self.name
35    }
36
37    pub fn set_name(&mut self, name: impl Into<PackageName>) {
38        self.name = name.into();
39    }
40
41    /// Add an `Interface` to the package
42    pub fn interface(&mut self, interface: Interface) {
43        self.items.push(PackageItem::Interface(interface))
44    }
45
46    /// Add a `World` to the package
47    pub fn world(&mut self, world: World) {
48        self.items.push(PackageItem::World(world))
49    }
50
51    pub fn item(&mut self, item: impl Into<PackageItem>) {
52        self.items.push(item.into());
53    }
54
55    pub fn items(&self) -> &[PackageItem] {
56        &self.items
57    }
58
59    pub fn items_mut(&mut self) -> &mut Vec<PackageItem> {
60        &mut self.items
61    }
62
63    /// Render the items of the package, without any package declaration.
64    fn render_items(&self, f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
65        for item in &self.items {
66            write!(f, "\n")?;
67            match item {
68                PackageItem::Interface(interface) => {
69                    if let Some(docs) = &interface.docs {
70                        docs.render(f, opts)?;
71                    }
72                    write!(f, "{}interface {} {{", opts.spaces(), interface.name)?;
73                    if !interface.uses.is_empty() || !interface.items.is_empty() {
74                        write!(f, "\n")?;
75                        interface.uses.render(f, &opts.indent())?;
76                        interface.items.render(f, &opts.indent())?;
77                        write!(f, "{}}}\n", opts.spaces())?;
78                    } else {
79                        write!(f, "}}\n")?;
80                    }
81                }
82                PackageItem::World(world) => {
83                    world.render(f, opts)?;
84                }
85            }
86        }
87        Ok(())
88    }
89}
90
91impl Render for Package {
92    fn render(&self, f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
93        write!(f, "{}package {};\n", opts.spaces(), self.name)?;
94        self.render_items(f, opts)
95    }
96}
97
98impl fmt::Display for Package {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        self.render(f, &RenderOpts::default())
101    }
102}
103
104/// A variant of [`Package`] that's rendered with a nested syntax.
105pub struct NestedPackage(Package);
106
107/// Provide associated functions explicitly.
108/// [`Package`] instance methods are provided via the [`Deref`] and [`DerefMut`] traits.
109impl NestedPackage {
110    /// Create a new instance of `NestedPackage`.
111    pub fn new(name: PackageName) -> Self {
112        Self(Package::new(name))
113    }
114}
115
116impl Deref for NestedPackage {
117    type Target = Package;
118
119    fn deref(&self) -> &Self::Target {
120        &self.0
121    }
122}
123
124impl DerefMut for NestedPackage {
125    fn deref_mut(&mut self) -> &mut Self::Target {
126        &mut self.0
127    }
128}
129
130impl Render for NestedPackage {
131    fn render(&self, f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
132        write!(f, "{}package {} {{\n", opts.spaces(), self.0.name)?;
133        self.0.render_items(f, &opts.indent())?;
134        write!(f, "{}}}\n", opts.spaces())
135    }
136}
137
138impl fmt::Display for NestedPackage {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        self.render(f, &RenderOpts::default())
141    }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
147pub enum PackageItem {
148    Interface(Interface),
149    World(World),
150}
151
152/// A structure used to keep track of the name of a package, containing optional
153/// information such as a namespace and version information.
154///
155/// This is directly encoded as an "ID" in the binary component representation
156/// with an interfaced tacked on as well.
157#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
160pub struct PackageName {
161    /// A namespace such as `wasi` in `wasi:foo/bar`
162    namespace: String,
163    /// The kebab-name of this package, which is always specified.
164    name: Ident,
165    /// Optional major/minor version information.
166    version: Option<Version>,
167}
168
169impl PackageName {
170    /// Create a new instance of `PackageName`
171    pub fn new(
172        namespace: impl Into<String>,
173        name: impl Into<Ident>,
174        version: Option<Version>,
175    ) -> Self {
176        Self {
177            namespace: namespace.into(),
178            name: name.into(),
179            version,
180        }
181    }
182
183    pub fn namespace(&self) -> &str {
184        &self.namespace
185    }
186
187    pub fn set_namespace(&mut self, namespace: impl Into<String>) {
188        self.namespace = namespace.into();
189    }
190
191    pub fn name(&self) -> &Ident {
192        &self.name
193    }
194
195    pub fn set_name(&mut self, name: impl Into<Ident>) {
196        self.name = name.into()
197    }
198
199    pub fn version(&self) -> Option<&Version> {
200        self.version.as_ref()
201    }
202
203    pub fn set_version(&mut self, version: Option<impl Into<Version>>) {
204        self.version = version.map(|v| v.into())
205    }
206}
207
208impl From<PackageName> for String {
209    fn from(name: PackageName) -> String {
210        name.to_string()
211    }
212}
213
214impl fmt::Display for PackageName {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        write!(f, "{}:{}", self.namespace, self.name)?;
217        if let Some(version) = &self.version {
218            write!(f, "@{version}")?;
219        }
220        Ok(())
221    }
222}