golem_wasm/analysis/
model.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::analysis::AnalysisResult;
16use std::fmt::{Display, Formatter};
17
18#[derive(Debug, Clone, PartialEq, Hash, Eq)]
19#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
20#[cfg_attr(feature = "host", serde(tag = "type"))]
21#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
22#[cfg_attr(feature = "host", derive(poem_openapi::Union))]
23#[cfg_attr(feature = "host", oai(discriminator_name = "type", one_of = true))]
24pub enum AnalysedExport {
25    Function(AnalysedFunction),
26    Instance(AnalysedInstance),
27}
28
29#[derive(Debug, Clone, PartialEq, Hash, Eq)]
30#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
31#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
32#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
33pub struct AnalysedFunction {
34    pub name: String,
35    pub parameters: Vec<AnalysedFunctionParameter>,
36    pub result: Option<AnalysedFunctionResult>,
37}
38
39impl AnalysedFunction {
40    pub fn is_constructor(&self) -> bool {
41        self.name.starts_with("[constructor]")
42            && self.result.is_some()
43            && matches!(
44                &self.result.as_ref().unwrap().typ,
45                AnalysedType::Handle(TypeHandle {
46                    mode: AnalysedResourceMode::Owned,
47                    ..
48                })
49            )
50    }
51
52    pub fn is_method(&self) -> bool {
53        self.name.starts_with("[method]")
54            && !self.parameters.is_empty()
55            && matches!(
56                &self.parameters[0].typ,
57                AnalysedType::Handle(TypeHandle {
58                    mode: AnalysedResourceMode::Borrowed,
59                    ..
60                })
61            )
62    }
63
64    pub fn is_static_method(&self) -> bool {
65        self.name.starts_with("[static]")
66    }
67}
68
69#[derive(Debug, Clone, PartialEq, Hash, Eq)]
70#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
71#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
72#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
73pub struct AnalysedInstance {
74    pub name: String,
75    pub functions: Vec<AnalysedFunction>,
76}
77
78#[derive(Debug, Clone, PartialEq, Hash, Eq)]
79#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
80#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
81#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
82pub struct TypeResult {
83    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
84    pub name: Option<String>,
85    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
86    pub owner: Option<String>,
87    pub ok: Option<Box<AnalysedType>>,
88    pub err: Option<Box<AnalysedType>>,
89}
90
91#[derive(Debug, Clone, PartialEq, Hash, Eq)]
92#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
93#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
94#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
95pub struct NameTypePair {
96    pub name: String,
97    pub typ: AnalysedType,
98}
99
100#[derive(Debug, Clone, PartialEq, Hash, Eq)]
101#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
102#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
103#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
104pub struct NameOptionTypePair {
105    pub name: String,
106    pub typ: Option<AnalysedType>,
107}
108
109#[derive(Debug, Clone, PartialEq, Hash, Eq)]
110#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
111#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
112#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
113pub struct TypeVariant {
114    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
115    pub name: Option<String>,
116    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
117    pub owner: Option<String>,
118    pub cases: Vec<NameOptionTypePair>,
119}
120
121#[derive(Debug, Clone, PartialEq, Hash, Eq)]
122#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
123#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
124#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
125pub struct TypeOption {
126    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
127    pub name: Option<String>,
128    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
129    pub owner: Option<String>,
130    pub inner: Box<AnalysedType>,
131}
132
133#[derive(Debug, Clone, PartialEq, Hash, Eq)]
134#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
135#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
136#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
137pub struct TypeEnum {
138    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
139    pub name: Option<String>,
140    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
141    pub owner: Option<String>,
142    pub cases: Vec<String>,
143}
144
145#[derive(Debug, Clone, PartialEq, Hash, Eq)]
146#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
147#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
148#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
149pub struct TypeFlags {
150    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
151    pub name: Option<String>,
152    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
153    pub owner: Option<String>,
154    pub names: Vec<String>,
155}
156
157#[derive(Debug, Clone, PartialEq, Hash, Eq)]
158#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
159#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
160#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
161pub struct TypeRecord {
162    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
163    pub name: Option<String>,
164    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
165    pub owner: Option<String>,
166    pub fields: Vec<NameTypePair>,
167}
168
169#[derive(Debug, Clone, PartialEq, Hash, Eq)]
170#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
171#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
172#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
173pub struct TypeTuple {
174    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
175    pub name: Option<String>,
176    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
177    pub owner: Option<String>,
178    pub items: Vec<AnalysedType>,
179}
180
181#[derive(Debug, Clone, PartialEq, Hash, Eq)]
182#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
183#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
184#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
185pub struct TypeList {
186    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
187    pub name: Option<String>,
188    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
189    pub owner: Option<String>,
190    pub inner: Box<AnalysedType>,
191}
192
193#[derive(Debug, Clone, PartialEq, Hash, Eq)]
194#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
195#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
196#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
197pub struct TypeStr;
198
199#[derive(Debug, Clone, PartialEq, Hash, Eq)]
200#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
201#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
202#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
203pub struct TypeChr;
204
205#[derive(Debug, Clone, PartialEq, Hash, Eq)]
206#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
207#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
208#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
209pub struct TypeF64;
210
211#[derive(Debug, Clone, PartialEq, Hash, Eq)]
212#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
213#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
214#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
215pub struct TypeF32;
216
217#[derive(Debug, Clone, PartialEq, Hash, Eq)]
218#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
219#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
220#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
221pub struct TypeU64;
222
223#[derive(Debug, Clone, PartialEq, Hash, Eq)]
224#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
225#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
226#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
227pub struct TypeS64;
228
229#[derive(Debug, Clone, PartialEq, Hash, Eq)]
230#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
231#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
232#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
233pub struct TypeU32;
234
235#[derive(Debug, Clone, PartialEq, Hash, Eq)]
236#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
237#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
238#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
239pub struct TypeS32;
240
241#[derive(Debug, Clone, PartialEq, Hash, Eq)]
242#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
243#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
244#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
245pub struct TypeU16;
246
247#[derive(Debug, Clone, PartialEq, Hash, Eq)]
248#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
249#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
250#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
251pub struct TypeS16;
252
253#[derive(Debug, Clone, PartialEq, Hash, Eq)]
254#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
255#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
256#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
257pub struct TypeU8;
258
259#[derive(Debug, Clone, PartialEq, Hash, Eq)]
260#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
261#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
262#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
263pub struct TypeS8;
264
265#[derive(Debug, Clone, PartialEq, Hash, Eq)]
266#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
267#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
268#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
269pub struct TypeBool;
270
271#[derive(Debug, Clone, PartialEq, Hash, Eq)]
272#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
273#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
274#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
275pub struct TypeHandle {
276    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
277    pub name: Option<String>,
278    #[cfg_attr(feature = "host", serde(skip_serializing_if = "Option::is_none"))]
279    pub owner: Option<String>,
280    pub resource_id: AnalysedResourceId,
281    pub mode: AnalysedResourceMode,
282}
283
284#[derive(Debug, Clone, Hash, PartialEq, Eq)]
285#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
286#[cfg_attr(feature = "host", serde(tag = "type"))]
287#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
288#[cfg_attr(feature = "host", derive(poem_openapi::Union))]
289#[cfg_attr(feature = "host", oai(discriminator_name = "type", one_of = true))]
290pub enum AnalysedType {
291    Variant(TypeVariant),
292    Result(TypeResult),
293    Option(TypeOption),
294    Enum(TypeEnum),
295    Flags(TypeFlags),
296    Record(TypeRecord),
297    Tuple(TypeTuple),
298    List(TypeList),
299    Str(TypeStr),
300    Chr(TypeChr),
301    F64(TypeF64),
302    F32(TypeF32),
303    U64(TypeU64),
304    S64(TypeS64),
305    U32(TypeU32),
306    S32(TypeS32),
307    U16(TypeU16),
308    S16(TypeS16),
309    U8(TypeU8),
310    S8(TypeS8),
311    Bool(TypeBool),
312    Handle(TypeHandle),
313}
314
315impl AnalysedType {
316    pub fn name(&self) -> Option<&str> {
317        match self {
318            AnalysedType::Variant(typ) => typ.name.as_deref(),
319            AnalysedType::Result(typ) => typ.name.as_deref(),
320            AnalysedType::Option(typ) => typ.name.as_deref(),
321            AnalysedType::Enum(typ) => typ.name.as_deref(),
322            AnalysedType::Flags(typ) => typ.name.as_deref(),
323            AnalysedType::Record(typ) => typ.name.as_deref(),
324            AnalysedType::Tuple(typ) => typ.name.as_deref(),
325            AnalysedType::List(typ) => typ.name.as_deref(),
326            AnalysedType::Handle(typ) => typ.name.as_deref(),
327            _ => None,
328        }
329    }
330
331    pub fn with_optional_name(self, name: Option<String>) -> Self {
332        match self {
333            AnalysedType::Variant(mut typ) => {
334                typ.name = name;
335                AnalysedType::Variant(typ)
336            }
337            AnalysedType::Result(mut typ) => {
338                typ.name = name;
339                AnalysedType::Result(typ)
340            }
341            AnalysedType::Option(mut typ) => {
342                typ.name = name;
343                AnalysedType::Option(typ)
344            }
345            AnalysedType::Enum(mut typ) => {
346                typ.name = name;
347                AnalysedType::Enum(typ)
348            }
349            AnalysedType::Flags(mut typ) => {
350                typ.name = name;
351                AnalysedType::Flags(typ)
352            }
353            AnalysedType::Record(mut typ) => {
354                typ.name = name;
355                AnalysedType::Record(typ)
356            }
357            AnalysedType::Tuple(mut typ) => {
358                typ.name = name;
359                AnalysedType::Tuple(typ)
360            }
361            AnalysedType::List(mut typ) => {
362                typ.name = name;
363                AnalysedType::List(typ)
364            }
365            AnalysedType::Handle(mut typ) => {
366                typ.name = name;
367                AnalysedType::Handle(typ)
368            }
369            _ => self,
370        }
371    }
372
373    pub fn named(self, name: impl AsRef<str>) -> Self {
374        self.with_optional_name(Some(name.as_ref().to_string()))
375    }
376
377    pub fn owner(&self) -> Option<&str> {
378        match self {
379            AnalysedType::Variant(typ) => typ.owner.as_deref(),
380            AnalysedType::Result(typ) => typ.owner.as_deref(),
381            AnalysedType::Option(typ) => typ.owner.as_deref(),
382            AnalysedType::Enum(typ) => typ.owner.as_deref(),
383            AnalysedType::Flags(typ) => typ.owner.as_deref(),
384            AnalysedType::Record(typ) => typ.owner.as_deref(),
385            AnalysedType::Tuple(typ) => typ.owner.as_deref(),
386            AnalysedType::List(typ) => typ.owner.as_deref(),
387            AnalysedType::Handle(typ) => typ.owner.as_deref(),
388            _ => None,
389        }
390    }
391
392    pub fn with_optional_owner(self, owner: Option<String>) -> Self {
393        match self {
394            AnalysedType::Variant(mut typ) => {
395                typ.owner = owner;
396                AnalysedType::Variant(typ)
397            }
398            AnalysedType::Result(mut typ) => {
399                typ.owner = owner;
400                AnalysedType::Result(typ)
401            }
402            AnalysedType::Option(mut typ) => {
403                typ.owner = owner;
404                AnalysedType::Option(typ)
405            }
406            AnalysedType::Enum(mut typ) => {
407                typ.owner = owner;
408                AnalysedType::Enum(typ)
409            }
410            AnalysedType::Flags(mut typ) => {
411                typ.owner = owner;
412                AnalysedType::Flags(typ)
413            }
414            AnalysedType::Record(mut typ) => {
415                typ.owner = owner;
416                AnalysedType::Record(typ)
417            }
418            AnalysedType::Tuple(mut typ) => {
419                typ.owner = owner;
420                AnalysedType::Tuple(typ)
421            }
422            AnalysedType::List(mut typ) => {
423                typ.owner = owner;
424                AnalysedType::List(typ)
425            }
426            AnalysedType::Handle(mut typ) => {
427                typ.owner = owner;
428                AnalysedType::Handle(typ)
429            }
430            _ => self,
431        }
432    }
433
434    pub fn owned(self, owner: impl AsRef<str>) -> Self {
435        self.with_optional_owner(Some(owner.as_ref().to_string()))
436    }
437
438    pub fn contains_handle(&self) -> bool {
439        match self {
440            AnalysedType::Handle(_) => true,
441            AnalysedType::Variant(typ) => typ
442                .cases
443                .iter()
444                .any(|case| case.typ.as_ref().is_some_and(|t| t.contains_handle())),
445            AnalysedType::Result(typ) => {
446                typ.ok.as_ref().is_some_and(|t| t.contains_handle())
447                    || typ.err.as_ref().is_some_and(|t| t.contains_handle())
448            }
449            AnalysedType::Option(typ) => typ.inner.contains_handle(),
450            AnalysedType::Record(typ) => typ.fields.iter().any(|f| f.typ.contains_handle()),
451            AnalysedType::Tuple(typ) => typ.items.iter().any(|t| t.contains_handle()),
452            AnalysedType::List(typ) => typ.inner.contains_handle(),
453            _ => false,
454        }
455    }
456}
457
458pub mod analysed_type {
459    use crate::analysis::*;
460
461    pub fn field(name: &str, typ: AnalysedType) -> NameTypePair {
462        NameTypePair {
463            name: name.to_string(),
464            typ,
465        }
466    }
467
468    pub fn case(name: &str, typ: AnalysedType) -> NameOptionTypePair {
469        NameOptionTypePair {
470            name: name.to_string(),
471            typ: Some(typ),
472        }
473    }
474
475    pub fn opt_case(name: &str, typ: Option<AnalysedType>) -> NameOptionTypePair {
476        NameOptionTypePair {
477            name: name.to_string(),
478            typ,
479        }
480    }
481
482    pub fn unit_case(name: &str) -> NameOptionTypePair {
483        NameOptionTypePair {
484            name: name.to_string(),
485            typ: None,
486        }
487    }
488
489    pub fn bool() -> AnalysedType {
490        AnalysedType::Bool(TypeBool)
491    }
492
493    pub fn s8() -> AnalysedType {
494        AnalysedType::S8(TypeS8)
495    }
496
497    pub fn s16() -> AnalysedType {
498        AnalysedType::S16(TypeS16)
499    }
500
501    pub fn s32() -> AnalysedType {
502        AnalysedType::S32(TypeS32)
503    }
504
505    pub fn s64() -> AnalysedType {
506        AnalysedType::S64(TypeS64)
507    }
508
509    pub fn u8() -> AnalysedType {
510        AnalysedType::U8(TypeU8)
511    }
512
513    pub fn u16() -> AnalysedType {
514        AnalysedType::U16(TypeU16)
515    }
516
517    pub fn u32() -> AnalysedType {
518        AnalysedType::U32(TypeU32)
519    }
520
521    pub fn u64() -> AnalysedType {
522        AnalysedType::U64(TypeU64)
523    }
524
525    pub fn f32() -> AnalysedType {
526        AnalysedType::F32(TypeF32)
527    }
528
529    pub fn f64() -> AnalysedType {
530        AnalysedType::F64(TypeF64)
531    }
532
533    pub fn chr() -> AnalysedType {
534        AnalysedType::Chr(TypeChr)
535    }
536
537    pub fn str() -> AnalysedType {
538        AnalysedType::Str(TypeStr)
539    }
540
541    pub fn list(inner: AnalysedType) -> AnalysedType {
542        AnalysedType::List(TypeList {
543            name: None,
544            owner: None,
545            inner: Box::new(inner),
546        })
547    }
548
549    pub fn option(inner: AnalysedType) -> AnalysedType {
550        AnalysedType::Option(TypeOption {
551            name: None,
552            owner: None,
553            inner: Box::new(inner),
554        })
555    }
556
557    pub fn flags(names: &[&str]) -> AnalysedType {
558        AnalysedType::Flags(TypeFlags {
559            name: None,
560            owner: None,
561            names: names.iter().map(|n| n.to_string()).collect(),
562        })
563    }
564
565    pub fn r#enum(cases: &[&str]) -> AnalysedType {
566        AnalysedType::Enum(TypeEnum {
567            name: None,
568            owner: None,
569            cases: cases.iter().map(|n| n.to_string()).collect(),
570        })
571    }
572
573    pub fn tuple(items: Vec<AnalysedType>) -> AnalysedType {
574        AnalysedType::Tuple(TypeTuple {
575            name: None,
576            owner: None,
577            items,
578        })
579    }
580
581    pub fn result(ok: AnalysedType, err: AnalysedType) -> AnalysedType {
582        AnalysedType::Result(TypeResult {
583            name: None,
584            owner: None,
585            ok: Some(Box::new(ok)),
586            err: Some(Box::new(err)),
587        })
588    }
589
590    pub fn result_ok(ok: AnalysedType) -> AnalysedType {
591        AnalysedType::Result(TypeResult {
592            name: None,
593            owner: None,
594            ok: Some(Box::new(ok)),
595            err: None,
596        })
597    }
598
599    pub fn result_err(err: AnalysedType) -> AnalysedType {
600        AnalysedType::Result(TypeResult {
601            name: None,
602            owner: None,
603            ok: None,
604            err: Some(Box::new(err)),
605        })
606    }
607
608    pub fn record(fields: Vec<NameTypePair>) -> AnalysedType {
609        AnalysedType::Record(TypeRecord {
610            name: None,
611            owner: None,
612            fields,
613        })
614    }
615
616    pub fn variant(cases: Vec<NameOptionTypePair>) -> AnalysedType {
617        AnalysedType::Variant(TypeVariant {
618            name: None,
619            owner: None,
620            cases,
621        })
622    }
623
624    pub fn handle(resource_id: AnalysedResourceId, mode: AnalysedResourceMode) -> AnalysedType {
625        AnalysedType::Handle(TypeHandle {
626            name: None,
627            owner: None,
628            resource_id,
629            mode,
630        })
631    }
632}
633
634#[derive(Debug, Clone, PartialEq, Hash, Eq)]
635#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
636#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
637#[cfg_attr(feature = "host", derive(poem_openapi::Enum))]
638pub enum AnalysedResourceMode {
639    Owned,
640    Borrowed,
641}
642
643#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
644#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
645#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
646#[cfg_attr(feature = "host", derive(poem_openapi::NewType))]
647pub struct AnalysedResourceId(pub u64);
648
649#[derive(Debug, Clone, PartialEq, Hash, Eq)]
650#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
651#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
652#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
653pub struct AnalysedFunctionParameter {
654    pub name: String,
655    pub typ: AnalysedType,
656}
657
658#[derive(Debug, Clone, PartialEq, Hash, Eq)]
659#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
660#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
661#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
662pub struct AnalysedFunctionResult {
663    pub typ: AnalysedType,
664}
665
666#[derive(Debug, Clone)]
667#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
668#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
669#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
670pub struct InterfaceCouldNotBeAnalyzedWarning {
671    pub name: String,
672    pub failure: AnalysisFailure,
673}
674
675#[derive(Debug, Clone)]
676#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
677#[cfg_attr(feature = "host", serde(tag = "type"))]
678#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
679#[cfg_attr(feature = "host", derive(poem_openapi::Union))]
680#[cfg_attr(feature = "host", oai(discriminator_name = "type", one_of = true))]
681pub enum AnalysisWarning {
682    InterfaceCouldNotBeAnalyzed(InterfaceCouldNotBeAnalyzedWarning),
683}
684
685impl Display for AnalysisWarning {
686    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
687        match self {
688            AnalysisWarning::InterfaceCouldNotBeAnalyzed(warning) => {
689                write!(
690                    f,
691                    "Interface could not be analyzed: {} {}",
692                    warning.name, warning.failure.reason
693                )
694            }
695        }
696    }
697}
698
699#[derive(Debug, Clone)]
700#[cfg_attr(feature = "host", derive(serde::Serialize, serde::Deserialize))]
701#[cfg_attr(feature = "host", derive(bincode::Encode, bincode::Decode))]
702#[cfg_attr(feature = "host", derive(poem_openapi::Object))]
703pub struct AnalysisFailure {
704    pub reason: String,
705}
706
707impl AnalysisFailure {
708    pub fn failed(message: impl Into<String>) -> AnalysisFailure {
709        AnalysisFailure {
710            reason: message.into(),
711        }
712    }
713
714    pub fn fail_on_missing<T>(value: Option<T>, description: impl AsRef<str>) -> AnalysisResult<T> {
715        match value {
716            Some(value) => Ok(value),
717            None => Err(AnalysisFailure::failed(format!(
718                "Missing {}",
719                description.as_ref()
720            ))),
721        }
722    }
723}
724
725#[cfg(test)]
726mod tests {
727    use crate::analysis::analysed_type::{bool, list, str};
728    use crate::analysis::{
729        AnalysedExport, AnalysedFunction, AnalysedFunctionParameter, AnalysedFunctionResult,
730        AnalysedInstance,
731    };
732    use poem_openapi::types::ToJSON;
733    use test_r::test;
734
735    #[cfg(feature = "host")]
736    #[cfg(feature = "host")]
737    #[test]
738    fn analysed_export_poem_and_serde_are_compatible() {
739        let export1 = AnalysedExport::Instance(AnalysedInstance {
740            name: "inst1".to_string(),
741            functions: vec![AnalysedFunction {
742                name: "func1".to_string(),
743                parameters: vec![AnalysedFunctionParameter {
744                    name: "param1".to_string(),
745                    typ: bool(),
746                }],
747                result: Some(AnalysedFunctionResult { typ: list(str()) }),
748            }],
749        });
750        let poem_serialized = export1.to_json_string();
751        let serde_deserialized: AnalysedExport = serde_json::from_str(&poem_serialized).unwrap();
752
753        assert_eq!(export1, serde_deserialized);
754    }
755}