summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/interface/namespace.rs
blob: 4a57d0ff41a6416a66db68c50d13a86909f3aa49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! # Namespace definition for a `ComponentInterface`.
//!
//! This module converts a namespace definition from UDL into structures that
//! can be added to a `ComponentInterface`.
//!
//! In WebIDL proper, each `namespace` declares a set of functions and attributes that
//! are exposed as a global object of that name, and there can be any number of such
//! namespace definitions.
//!
//! For our purposes with UDL, we expect just a single `namespace` declaration, which
//! defines properties of the component as a whole (currently just the name). It also
//! contains the functions that will be exposed as individual plain functions exported by
//! the component, if any. So something like this:
//!
//! ```
//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
//! namespace example {
//!   string hello();
//! };
//! # "##)?;
//! # Ok::<(), anyhow::Error>(())
//! ```
//!
//! Declares a component named "example" with a single exported function named "hello":
//!
//! ```
//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
//! # namespace example {
//! #   string hello();
//! # };
//! # "##)?;
//! assert_eq!(ci.namespace(), "example");
//! assert_eq!(ci.get_function_definition("hello").unwrap().name(), "hello");
//! # Ok::<(), anyhow::Error>(())
//! ```
//!
//! While this awkward-looking syntax:
//!
//! ```
//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
//! namespace example {};
//! # "##)?;
//! # Ok::<(), anyhow::Error>(())
//! ```
//!
//! Declares a component named "example" with no exported functions:
//!
//! ```
//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
//! # namespace example {};
//! # "##)?;
//! assert_eq!(ci.namespace(), "example");
//! assert_eq!(ci.function_definitions().len(), 0);
//! # Ok::<(), anyhow::Error>(())
//! ```
//!
//! Yeah, it's a bit of an awkward fit syntactically, but it's enough
//! to get us up and running for a first version of this tool.

use anyhow::{bail, Result};

use super::{APIBuilder, APIConverter, ComponentInterface};

/// A namespace is currently just a name, but might hold more metadata about
/// the component in future.
///
#[derive(Debug, Clone, Hash)]
pub struct Namespace {
    pub(super) name: String,
}

impl APIBuilder for weedle::NamespaceDefinition<'_> {
    fn process(&self, ci: &mut ComponentInterface) -> Result<()> {
        if self.attributes.is_some() {
            bail!("namespace attributes are not supported yet");
        }
        ci.add_namespace_definition(Namespace {
            name: self.identifier.0.to_string(),
        })?;
        for func in self.members.body.convert(ci)? {
            ci.add_function_definition(func)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_empty_namespace() {
        const UDL: &str = r#"
            namespace foobar{};
        "#;
        let ci = ComponentInterface::from_webidl(UDL).unwrap();
        assert_eq!(ci.namespace(), "foobar");
    }

    #[test]
    fn test_namespace_with_functions() {
        const UDL: &str = r#"
            namespace foobar{
                boolean hello();
                void world();
            };
        "#;
        let ci = ComponentInterface::from_webidl(UDL).unwrap();
        assert_eq!(ci.namespace(), "foobar");
        assert_eq!(ci.function_definitions().len(), 2);
        assert!(ci.get_function_definition("hello").is_some());
        assert!(ci.get_function_definition("world").is_some());
        assert!(ci.get_function_definition("potato").is_none());
    }

    #[test]
    fn test_rejects_duplicate_namespaces() {
        const UDL: &str = r#"
            namespace foobar{
                boolean hello();
                void world();
            };
            namespace something_else{};
        "#;
        let err = ComponentInterface::from_webidl(UDL).unwrap_err();
        assert_eq!(err.to_string(), "duplicate namespace definition");
    }
}