linera_witty/wit_generation/
mod.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Generation of WIT files.
5
6mod stub_instance;
7
8use std::collections::BTreeMap;
9
10use genawaiter::{rc::gen, yield_};
11
12pub use self::stub_instance::StubInstance;
13pub use crate::type_traits::RegisterWitTypes;
14
15/// Generates WIT snippets for an interface.
16pub trait WitInterface {
17    /// The [`WitType`][`crate::WitType`]s that this interface uses.
18    type Dependencies: RegisterWitTypes;
19
20    /// The name of the package the interface belongs to.
21    fn wit_package() -> &'static str;
22
23    /// The name of the interface.
24    fn wit_name() -> &'static str;
25
26    /// The WIT definitions of each function in this interface.
27    fn wit_functions() -> Vec<String>;
28}
29
30/// Helper type to write a [`WitInterface`] to a file.
31#[derive(Clone, Debug)]
32pub struct WitInterfaceWriter {
33    package: &'static str,
34    name: &'static str,
35    types: BTreeMap<String, String>,
36    functions: Vec<String>,
37}
38
39impl WitInterfaceWriter {
40    /// Prepares a new [`WitInterfaceWriter`] to write the provided `Interface`.
41    pub fn new<Interface>() -> Self
42    where
43        Interface: WitInterface,
44    {
45        let mut types = BTreeMap::new();
46
47        Interface::Dependencies::register_wit_types(&mut types);
48
49        WitInterfaceWriter {
50            package: Interface::wit_package(),
51            name: Interface::wit_name(),
52            types,
53            functions: Interface::wit_functions(),
54        }
55    }
56
57    /// Returns an [`Iterator`] with the file contents of the WIT interface file.
58    pub fn generate_file_contents(&self) -> impl Iterator<Item = &str> {
59        gen!({
60            yield_!("package ");
61            yield_!(self.package);
62            yield_!(";\n\n");
63
64            yield_!("interface ");
65            yield_!(self.name);
66            yield_!(" {\n");
67
68            for function in &self.functions {
69                yield_!(&function);
70                yield_!("\n");
71            }
72
73            for type_declaration in self.types.values() {
74                if !type_declaration.is_empty() {
75                    yield_!("\n");
76                    yield_!(&type_declaration);
77                }
78            }
79
80            yield_!("}\n");
81        })
82        .into_iter()
83    }
84}
85
86/// Helper type to write a WIT file declaring a
87/// [world](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds).
88#[derive(Clone, Debug)]
89pub struct WitWorldWriter {
90    package: Option<&'static str>,
91    name: String,
92    imports: Vec<&'static str>,
93    exports: Vec<&'static str>,
94}
95
96impl WitWorldWriter {
97    /// Creates a new [`WitWorldWriter`] to write a world with the provided `name`.
98    pub fn new(package: impl Into<Option<&'static str>>, name: impl Into<String>) -> Self {
99        WitWorldWriter {
100            package: package.into(),
101            name: name.into(),
102            imports: Vec::new(),
103            exports: Vec::new(),
104        }
105    }
106
107    /// Registers a [`WitInterface`] to be imported into this world.
108    pub fn import<Interface>(mut self) -> Self
109    where
110        Interface: WitInterface,
111    {
112        self.imports.push(Interface::wit_name());
113        self
114    }
115
116    /// Registers a [`WitInterface`] to be exported from this world.
117    pub fn export<Interface>(mut self) -> Self
118    where
119        Interface: WitInterface,
120    {
121        self.exports.push(Interface::wit_name());
122        self
123    }
124
125    /// Returns an [`Iterator`] with the file contents of the WIT world file, optionally including
126    /// a package header.
127    pub fn generate_file_contents(&self) -> impl Iterator<Item = &str> {
128        gen!({
129            if let Some(package) = &self.package {
130                yield_!("package ");
131                yield_!(package);
132                yield_!(";\n\n");
133            }
134
135            yield_!("world ");
136            yield_!(&self.name);
137            yield_!(" {\n");
138
139            for import in &self.imports {
140                yield_!("    import ");
141                yield_!(import);
142                yield_!(";\n");
143            }
144
145            if !self.imports.is_empty() {
146                yield_!("\n");
147            }
148
149            for export in &self.exports {
150                yield_!("    export ");
151                yield_!(export);
152                yield_!(";\n");
153            }
154
155            yield_!("}\n");
156        })
157        .into_iter()
158    }
159}