Skip to main content

geam_core/plan/host/
module.rs

1use super::HostFunctionTemplate;
2use crate::plan::{
3    ConstantTemplates, CustomTypeDefinition, ExternalTypeDefinition, FunctionTemplate, ModuleId,
4    SourceContext,
5};
6use ecow::EcoString;
7
8pub struct HostedPlannedModule {
9    id: ModuleId,
10    package: EcoString,
11    module: EcoString,
12    source_context: Option<SourceContext>,
13    custom_types: Vec<CustomTypeDefinition>,
14    external_types: Vec<ExternalTypeDefinition>,
15    constants: ConstantTemplates,
16    functions: Vec<HostedFunctionTemplate>,
17    anonymous_functions: Vec<FunctionTemplate>,
18}
19
20pub enum HostedFunctionTemplate {
21    GleamBody(Box<FunctionTemplate>),
22    HostTemplate(Box<HostFunctionTemplate>),
23}
24
25pub(crate) struct HostedPlannedModuleParts {
26    pub(crate) id: ModuleId,
27    pub(crate) package: EcoString,
28    pub(crate) module: EcoString,
29    pub(crate) source_context: Option<SourceContext>,
30    pub(crate) custom_types: Vec<CustomTypeDefinition>,
31    pub(crate) external_types: Vec<ExternalTypeDefinition>,
32    pub(crate) constants: ConstantTemplates,
33    pub(crate) functions: Vec<HostedFunctionTemplate>,
34    pub(crate) anonymous_functions: Vec<FunctionTemplate>,
35}
36
37impl HostedPlannedModule {
38    pub(crate) fn new(parts: HostedPlannedModuleParts) -> Self {
39        Self {
40            id: parts.id,
41            package: parts.package,
42            module: parts.module,
43            source_context: parts.source_context,
44            custom_types: parts.custom_types,
45            external_types: parts.external_types,
46            constants: parts.constants,
47            functions: parts.functions,
48            anonymous_functions: parts.anonymous_functions,
49        }
50    }
51
52    pub fn id(&self) -> ModuleId {
53        self.id
54    }
55
56    pub fn package(&self) -> &EcoString {
57        &self.package
58    }
59
60    pub fn module(&self) -> &EcoString {
61        &self.module
62    }
63
64    pub fn source_context(&self) -> Option<&SourceContext> {
65        self.source_context.as_ref()
66    }
67
68    pub fn functions(&self) -> &[HostedFunctionTemplate] {
69        &self.functions
70    }
71
72    pub fn external_types(&self) -> &[ExternalTypeDefinition] {
73        &self.external_types
74    }
75
76    pub(crate) fn custom_types(&self) -> &[CustomTypeDefinition] {
77        &self.custom_types
78    }
79
80    pub(crate) fn into_parts(self) -> HostedPlannedModuleParts {
81        HostedPlannedModuleParts {
82            id: self.id,
83            package: self.package,
84            module: self.module,
85            source_context: self.source_context,
86            custom_types: self.custom_types,
87            external_types: self.external_types,
88            constants: self.constants,
89            functions: self.functions,
90            anonymous_functions: self.anonymous_functions,
91        }
92    }
93}
94
95impl HostedFunctionTemplate {
96    pub(crate) fn name(&self) -> &EcoString {
97        match self {
98            Self::GleamBody(function) => function.name(),
99            Self::HostTemplate(function) => function.name(),
100        }
101    }
102
103    pub(crate) fn signature(&self) -> &crate::plan::FunctionTemplateSignature {
104        match self {
105            Self::GleamBody(function) => function.signature(),
106            Self::HostTemplate(function) => function.signature(),
107        }
108    }
109
110    pub fn gleam_body(&self) -> Option<&FunctionTemplate> {
111        match self {
112            Self::GleamBody(function) => Some(function.as_ref()),
113            Self::HostTemplate(_) => None,
114        }
115    }
116
117    pub fn host_template(&self) -> Option<&HostFunctionTemplate> {
118        match self {
119            Self::GleamBody(_) => None,
120            Self::HostTemplate(function) => Some(function.as_ref()),
121        }
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use crate::frontend::{ModuleSource, PackageSource, compile_typed_host_program};
128    use crate::host::{HostModule, HostProviderSet};
129    use crate::planner::plan_host_program;
130    use num_bigint::BigInt;
131
132    #[test]
133    fn function_templates_expose_exactly_one_body_owner() {
134        let hosts = HostProviderSet::new([HostModule::new("host_support", "host/math")
135            .expect("host module should be valid")
136            .with_function("identity", |value: BigInt| value)
137            .expect("host function should be valid")])
138        .expect("host modules should be unique");
139        let source = r#"
140import host/math
141
142pub fn main() {
143  math.identity(1)
144}
145"#;
146        let typed = compile_typed_host_program(
147            "application",
148            "main",
149            [PackageSource::new(
150                "application",
151                ["host_support"],
152                [ModuleSource::new("main", "main.gleam", source)],
153            )],
154            hosts,
155        )
156        .expect("host program should compile");
157        let plan = plan_host_program(typed).expect("host program should plan");
158        let host = &plan.modules()[0].functions()[0];
159        let source = &plan.modules()[1].functions()[0];
160
161        assert_eq!(
162            host.host_template()
163                .expect("source-less function should own a host template")
164                .name(),
165            "identity",
166        );
167        assert!(host.gleam_body().is_none());
168        assert_eq!(
169            source
170                .gleam_body()
171                .expect("source function should own a Gleam body")
172                .name(),
173            "main",
174        );
175        assert!(source.host_template().is_none());
176    }
177}