wit_encoder/
package.rs

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