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