weld_codegen/
codegen_rust.rs

1//! Rust language code-generator
2//!
3use std::{
4    collections::{BTreeMap, HashMap},
5    fmt::Write as _,
6    path::Path,
7    str::FromStr,
8    string::ToString,
9};
10
11use atelier_core::{
12    model::{
13        shapes::{
14            AppliedTraits, HasTraits, ListOrSet, Map as MapShape, MemberShape, Operation, Service,
15            ShapeKind, Simple, StructureOrUnion,
16        },
17        values::Value,
18        HasIdentity, Identifier, Model, NamespaceID, ShapeID,
19    },
20    prelude::{
21        prelude_namespace_id, prelude_shape_named, PRELUDE_NAMESPACE, SHAPE_BIGDECIMAL,
22        SHAPE_BIGINTEGER, SHAPE_BLOB, SHAPE_BOOLEAN, SHAPE_BYTE, SHAPE_DOCUMENT, SHAPE_DOUBLE,
23        SHAPE_FLOAT, SHAPE_INTEGER, SHAPE_LONG, SHAPE_PRIMITIVEBOOLEAN, SHAPE_PRIMITIVEBYTE,
24        SHAPE_PRIMITIVEDOUBLE, SHAPE_PRIMITIVEFLOAT, SHAPE_PRIMITIVEINTEGER, SHAPE_PRIMITIVELONG,
25        SHAPE_PRIMITIVESHORT, SHAPE_SHORT, SHAPE_STRING, SHAPE_TIMESTAMP, TRAIT_DEPRECATED,
26        TRAIT_DOCUMENTATION, TRAIT_TRAIT, TRAIT_UNSTABLE,
27    },
28};
29
30#[cfg(feature = "wasmbus")]
31use crate::wasmbus_model::Wasmbus;
32use crate::{
33    config::{LanguageConfig, OutputLanguage},
34    error::{print_warning, Error, Result},
35    format::{self, SourceFormatter},
36    gen::CodeGen,
37    model::{
38        codegen_rust_trait, get_operation, get_sorted_fields, get_trait, has_default,
39        is_opt_namespace, value_to_json, wasmcloud_model_namespace, CommentKind, PackageName, Ty,
40    },
41    render::Renderer,
42    wasmbus_model::CodegenRust,
43    writer::Writer,
44    BytesMut, JsonValue, ParamMap,
45};
46
47const WASMBUS_RPC_CRATE: &str = "wasmbus_rpc";
48const DEFAULT_MAP_TYPE: &str = "std::collections::HashMap";
49const DEFAULT_LIST_TYPE: &str = "Vec";
50const DEFAULT_SET_TYPE: &str = "std::collections::BTreeSet";
51
52/// declarations for sorting. First sort key is the type (simple, then map, then struct).
53/// In rust, sorting by BytesMut as the second key will result in sort by item name.
54#[derive(Eq, Ord, PartialOrd, PartialEq)]
55struct Declaration(u8, BytesMut);
56
57type ShapeList<'model> = Vec<(&'model ShapeID, &'model AppliedTraits, &'model ShapeKind)>;
58
59#[derive(Default)]
60pub struct RustCodeGen<'model> {
61    /// if set, limits declaration output to this namespace only
62    pub(crate) namespace: Option<NamespaceID>,
63    pub(crate) packages: HashMap<String, PackageName>,
64    pub(crate) import_core: String,
65    pub(crate) model: Option<&'model Model>,
66    pub(crate) with_lifetime: BTreeMap<ShapeID, bool>,
67}
68
69impl<'model> RustCodeGen<'model> {
70    pub fn new(model: Option<&'model Model>) -> Self {
71        Self {
72            model,
73            namespace: None,
74            packages: HashMap::default(),
75            import_core: String::default(),
76            with_lifetime: BTreeMap::default(),
77        }
78    }
79}
80
81struct ServiceInfo<'model> {
82    id: &'model Identifier,
83    traits: &'model AppliedTraits,
84    service: &'model Service,
85}
86
87impl<'model> ServiceInfo<'model> {
88    fn wasmbus_contract_id(&self) -> Option<String> {
89        match get_trait(self.traits, crate::model::wasmbus_trait()) {
90            Ok(Some(Wasmbus { contract_id: Some(contract_id), .. })) => Some(contract_id),
91            _ => None,
92        }
93    }
94}
95
96#[non_exhaustive]
97enum MethodArgFlags {
98    Normal,
99    // arg is type ToString
100    ToString,
101}
102
103/// Optional lifetime.
104/// When used as a parameter to a codegen function, the function adds the lifetime
105/// annotation to any identifiers generated _only if_ the identifier is required to have
106/// a lifetime. (based on self.has_lifetime(shape_id))
107#[derive(Copy)]
108pub(crate) enum Lifetime<'lt> {
109    None,        // no lifetime
110    Any,         // '_
111    L(&'lt str), // 'x (str contains the symbol only)
112}
113
114impl<'lt> Lifetime<'lt> {
115    /// returns one of `""`, `"<'_>"`, or `"<'x>"` where `x` is the L variant value
116    pub(crate) fn annotate(&self) -> String {
117        match self {
118            Lifetime::None => String::default(),
119            Lifetime::Any => "<'_>".to_string(),
120            Lifetime::L(s) => format!("<'{s}>"),
121        }
122    }
123
124    /// returns true if
125    pub(crate) fn is_some(&self) -> bool {
126        !matches!(self, Lifetime::None)
127    }
128}
129
130impl<'lt> Clone for Lifetime<'lt> {
131    fn clone(&self) -> Self {
132        *self
133    }
134}
135
136/// Returns true if the type is a rust primitive
137pub fn is_rust_primitive(id: &ShapeID) -> bool {
138    (id.namespace() == prelude_namespace_id()
139        && matches!(
140            id.shape_name().to_string().as_str(),
141            "Boolean" | "Byte" | "Short" | "Integer" | "Long" | "Float" | "Double"
142        ))
143        || (id.namespace() == wasmcloud_model_namespace()
144            && matches!(
145                id.shape_name().to_string().as_str(),
146                "U64" | "U32" | "U16" | "U8" | "I64" | "I32" | "I16" | "I8" | "F64" | "F32"
147            ))
148}
149
150impl<'model> CodeGen for RustCodeGen<'model> {
151    fn output_language(&self) -> OutputLanguage {
152        OutputLanguage::Rust
153    }
154
155    /// Initialize code generator and renderer for language output.j
156    /// This hook is called before any code is generated and can be used to initialize code generator
157    /// and/or perform additional processing before output files are created.
158    fn init(
159        &mut self,
160        model: Option<&Model>,
161        _lc: &LanguageConfig,
162        _output_dir: Option<&Path>,
163        _renderer: &mut Renderer,
164    ) -> std::result::Result<(), Error> {
165        self.namespace = None;
166        self.import_core = WASMBUS_RPC_CRATE.to_string();
167
168        if let Some(model) = model {
169            if let Some(Value::Array(codegen_min)) = model.metadata_value("codegen") {
170                let current_ver =
171                    semver::Version::parse(env!("CARGO_PKG_VERSION")).map_err(|e| {
172                        Error::InvalidModel(format!(
173                            "parse error for weld-codegen package version: {e}"
174                        ))
175                    })?;
176                for val in codegen_min.iter() {
177                    if let Value::Object(map) = val {
178                        if let Some(Value::String(lang)) = map.get("language") {
179                            if lang.as_str() == "rust" {
180                                if let Some(Value::String(ver)) = map.get("min_version") {
181                                    let min_ver = semver::Version::parse(ver).map_err(|e| {
182                                        Error::InvalidModel(format!(
183                                            "metadata parse error for codegen {{ language=rust, \
184                                             min_version={ver} }}: {e}"
185                                        ))
186                                    })?;
187                                    if min_ver.gt(&current_ver) {
188                                        return Err(Error::Model(format!(
189                                            "model requires weld-codegen version >= {min_ver}"
190                                        )));
191                                    }
192                                } else {
193                                    return Err(Error::Model(
194                                        "missing 'min_version' in metadata.codegen for lang=rust"
195                                            .to_string(),
196                                    ));
197                                }
198                            }
199                        }
200                    }
201                }
202            }
203            if let Some(packages) = model.metadata_value("package") {
204                let packages: Vec<PackageName> = serde_json::from_value(value_to_json(packages))
205                    .map_err(|e| {
206                        Error::Model(format!(
207                            "invalid metadata format for package, expecting format \
208                             '[{{namespace:\"org.example\",crate:\"path::module\"}}]':  {e}"
209                        ))
210                    })?;
211                for p in packages.iter() {
212                    self.packages.insert(p.namespace.to_string(), p.clone());
213                }
214            }
215            self.map_lifetimes(model);
216        }
217        Ok(())
218    }
219
220    /// Set up go formatter based on 'rust.formatter' settings in codegen.toml,
221    /// or use default if formatter is undefined or empty
222    fn source_formatter(&self, mut args: Vec<String>) -> Result<Box<dyn SourceFormatter>> {
223        let formatter = if args.is_empty() {
224            RustSourceFormatter::default()
225        } else {
226            let program = args.remove(0);
227            RustSourceFormatter { program, args }
228        };
229        Ok(Box::new(formatter))
230    }
231
232    /// Perform any initialization required prior to code generation for a file
233    /// `model` may be used to check model metadata
234    /// `namespace` is the namespace in the model to generate
235    #[allow(unused_variables)]
236    fn init_file(
237        &mut self,
238        w: &mut Writer,
239        model: &Model,
240        file_config: &crate::config::OutputFile,
241        params: &ParamMap,
242    ) -> Result<()> {
243        self.namespace = match &file_config.namespace {
244            Some(ns) => Some(NamespaceID::from_str(ns)?),
245            None => None,
246        };
247        if let Some(ref ns) = self.namespace {
248            if self.packages.get(&ns.to_string()).is_none() {
249                print_warning(&format!(
250                    concat!(
251                        "no package metadata defined for namespace {}.",
252                        " Add a declaration like this at the top of fhe .smithy file: ",
253                        " metadata package = [ {{ namespace: \"{}\", crate: \"crate_name\" }} ]"
254                    ),
255                    ns, ns
256                ));
257            }
258        }
259        self.import_core = match params.get("crate") {
260            Some(JsonValue::String(c)) if c == WASMBUS_RPC_CRATE => "crate".to_string(),
261            _ => WASMBUS_RPC_CRATE.to_string(),
262        };
263        Ok(())
264    }
265
266    fn write_source_file_header(
267        &mut self,
268        w: &mut Writer,
269        model: &Model,
270        params: &ParamMap,
271    ) -> Result<()> {
272        write!(
273            w,
274            r#"// This file is @generated by wasmcloud/weld-codegen {}.
275               // It is not intended for manual editing.
276           "#,
277            env!("CARGO_PKG_VERSION"),
278        )
279        .unwrap();
280        if let Some(ns) = &self.namespace {
281            writeln!(w, "// namespace: {ns}").unwrap();
282        }
283        w.write(b"\n");
284
285        match &self.namespace {
286            Some(n) if n == wasmcloud_model_namespace() => {
287                // the base model has minimal dependencies
288                w.write("#[allow(unused_imports)] use serde::{{Deserialize, Serialize}};\n");
289                if !params.contains_key("no_serde") {
290                    // these dependencies are only needed if we codegen ser/deser
291                    // - which is always except for the codegen crate
292                    writeln!(w,
293                        r#"#[allow(unused_imports)] 
294                        use {}::{{cbor::{{Write,Encoder,Decoder}},error::{{RpcError,RpcResult}}}};"#,
295                        self.import_core,
296                    ).unwrap();
297                }
298            }
299            _ => {
300                // all others use standard frontmatter
301
302                // special case for imports:
303                // if the crate we are generating is "wasmbus_rpc" then we have to import it with "crate::".
304                writeln!(
305                    w,
306                    r#"
307                #[allow(unused_imports)]
308                use {}::{{
309                    cbor::*,
310                    common::{{
311                        Context, deserialize, Message, MessageFormat, message_format, 
312                        MessageDispatch, SendOpts, serialize, Transport,
313                    }},
314                    error::{{RpcError,RpcResult}},
315                    Timestamp,
316                }};
317                #[allow(unused_imports)]
318                use serde::{{Deserialize, Serialize}};
319                #[allow(unused_imports)]
320                use async_trait::async_trait;
321                #[allow(unused_imports)]
322                use std::{{borrow::Borrow, borrow::Cow, io::Write, string::ToString}};"#,
323                    &self.import_core,
324                )
325                .unwrap();
326            }
327        }
328        write!(
329            w,
330            "\n#[allow(dead_code)] pub const SMITHY_VERSION : &str = \"{}\";\n\n",
331            model.smithy_version()
332        )
333        .unwrap();
334        Ok(())
335    }
336
337    fn declare_types(&mut self, w: &mut Writer, model: &Model, params: &ParamMap) -> Result<()> {
338        let ns = self.namespace.clone();
339
340        let mut shapes = model
341            .shapes()
342            .filter(|s| is_opt_namespace(s.id(), &ns))
343            .map(|s| (s.id(), s.traits(), s.body()))
344            .collect::<ShapeList>();
345        // sort shapes (they are all in the same namespace if ns.is_some(), which is usually true)
346        shapes.sort_by_key(|v| v.0);
347
348        for (id, traits, shape) in shapes.into_iter() {
349            if let Some(cg) = get_trait::<CodegenRust>(traits, codegen_rust_trait())? {
350                if cg.skip {
351                    continue;
352                }
353            }
354            let lt = match self.has_lifetime(id) {
355                true => Lifetime::L("v"),
356                false => Lifetime::None,
357            };
358            match shape {
359                ShapeKind::Simple(simple) => {
360                    self.declare_simple_shape(w, id.shape_name(), traits, simple, lt)?;
361                }
362                ShapeKind::Map(map) => {
363                    self.declare_map_shape(w, id.shape_name(), traits, map, lt)?;
364                }
365                ShapeKind::List(list) => {
366                    self.declare_list_or_set_shape(
367                        w,
368                        id.shape_name(),
369                        traits,
370                        list,
371                        DEFAULT_LIST_TYPE,
372                        lt,
373                    )?;
374                }
375                ShapeKind::Set(set) => {
376                    self.declare_list_or_set_shape(
377                        w,
378                        id.shape_name(),
379                        traits,
380                        set,
381                        DEFAULT_SET_TYPE,
382                        lt,
383                    )?;
384                }
385                ShapeKind::Structure(strukt) => {
386                    self.declare_structure_shape(w, id, traits, strukt, lt)?;
387                }
388                ShapeKind::Union(strukt) => {
389                    self.declare_union_shape(w, id, traits, strukt, lt)?;
390                }
391                ShapeKind::Operation(_)
392                | ShapeKind::Resource(_)
393                | ShapeKind::Service(_)
394                | ShapeKind::Unresolved => {}
395            }
396
397            // If the shape is not a trait, and ser-deser isn't disabled, generate encoder and decoder
398            // It's ok to declare cbor shape encoders & decoders even if not used by
399            // this service's protocol version, because this shape might be used in other interfaces
400            // that _do_ use cbor, and because the cbor en-/de- coders won't be added
401            // to the compiler output of they aren't used
402            if !traits.contains_key(&prelude_shape_named(TRAIT_TRAIT).unwrap())
403                && !params.contains_key("no_serde")
404            //&& env!("CARGO_PKG_NAME") != "weld-codegen"
405            // wasmbus-rpc can't be as dependency of weld-codegen or it creates circular dependencies
406            {
407                self.declare_shape_encoder(w, id, shape)?;
408                self.declare_shape_decoder(w, id, shape)?;
409            }
410        }
411        Ok(())
412    }
413
414    fn write_services(&mut self, w: &mut Writer, model: &Model, _params: &ParamMap) -> Result<()> {
415        let ns = self.namespace.clone();
416        let mut services: Vec<(&ShapeID, &AppliedTraits, &ShapeKind)> = model
417            .shapes()
418            .filter(|s| is_opt_namespace(s.id(), &ns))
419            .map(|s| (s.id(), s.traits(), s.body()))
420            .collect();
421        // sort services in this namespace, so output order is deterministic
422        services.sort_by_key(|me| me.0);
423        for (id, traits, shape) in services.iter() {
424            if let ShapeKind::Service(service) = shape {
425                let service = ServiceInfo { id: id.shape_name(), service, traits };
426                self.write_service_interface(w, model, &service)?;
427                self.write_service_receiver(w, model, &service)?;
428                self.write_service_sender(w, model, &service)?;
429            }
430        }
431        Ok(())
432    }
433
434    /// Write a single-line comment
435    fn write_comment(&mut self, w: &mut Writer, kind: CommentKind, line: &str) {
436        w.write(match kind {
437            CommentKind::Documentation => "/// ",
438            CommentKind::Inner => "// ",
439            CommentKind::InQuote => "// ", // not applicable for Rust
440        });
441        w.write(line);
442        w.write(b"\n");
443    }
444
445    fn to_method_name_case(&self, name: &str) -> String {
446        crate::strings::to_snake_case(name)
447    }
448
449    fn to_field_name_case(&self, name: &str) -> String {
450        crate::strings::to_snake_case(name)
451    }
452
453    fn to_type_name_case(&self, name: &str) -> String {
454        crate::strings::to_pascal_case(name)
455    }
456
457    /// returns rust source file extension "rs"
458    fn get_file_extension(&self) -> &'static str {
459        "rs"
460    }
461}
462
463impl<'model> RustCodeGen<'model> {
464    /// populate `with_lifetime` with every shape that requires a lifetime declaration
465    /// Save both positive and negative results to minimize re-evaluating shapes.
466    /// at the moment, this only works for values that are input parameters,
467    /// because we can't return a ref to the vec created inside a receiver or sender-response
468    fn map_lifetime(
469        &mut self,
470        model: &Model,
471        id: &ShapeID,
472        _traits: &AppliedTraits,
473        shape: &ShapeKind,
474    ) -> bool {
475        if let Some(val) = self.with_lifetime.get(id) {
476            return *val;
477        }
478        // FUTURE: add codegenRust trait "borrowed" (or array of lifetimes?)
479        // and check that here. If we use array of Lifetimes, a struct
480        // could define more than one
481        // if has-trait-borrowed { self.with_lifetime.insert(); return true }
482        // then, get rid of special case code for DocumentRef,
483        // and add the borrowed/lifetime trait to wasmcloud-common.smithy
484        match shape {
485            ShapeKind::Simple(_) => {}
486            ShapeKind::Map(map) => {
487                let value = map.value().target();
488                if let Some(target) = model.shape(value) {
489                    if self.map_lifetime(model, value, target.traits(), target.body()) {
490                        self.with_lifetime.insert(id.clone(), true);
491                        return true;
492                    }
493                } else if self.has_lifetime(value) {
494                    self.with_lifetime.insert(id.clone(), true);
495                    return true;
496                }
497                let key = map.key().target();
498                if let Some(target) = model.shape(key) {
499                    if self.map_lifetime(model, key, target.traits(), target.body()) {
500                        self.with_lifetime.insert(id.clone(), true);
501                        return true;
502                    }
503                } else if self.has_lifetime(key) {
504                    self.with_lifetime.insert(id.clone(), true);
505                    return true;
506                }
507            }
508            ShapeKind::List(list_or_set) | ShapeKind::Set(list_or_set) => {
509                let member = list_or_set.member().target();
510                if let Some(target) = model.shape(member) {
511                    if self.map_lifetime(model, member, target.traits(), target.body()) {
512                        self.with_lifetime.insert(id.clone(), true);
513                        return true;
514                    }
515                } else if self.has_lifetime(member) {
516                    self.with_lifetime.insert(id.clone(), true);
517                    return true;
518                }
519            }
520            ShapeKind::Structure(struct_or_union) | ShapeKind::Union(struct_or_union) => {
521                for member in struct_or_union.members() {
522                    let field = member.target();
523                    if let Some(target) = model.shape(field) {
524                        if self.map_lifetime(model, field, target.traits(), target.body()) {
525                            self.with_lifetime.insert(id.clone(), true);
526                            return true;
527                        }
528                    } else if self.has_lifetime(field) {
529                        self.with_lifetime.insert(id.clone(), true);
530                        return true;
531                    }
532                }
533            }
534            ShapeKind::Operation(_)
535            | ShapeKind::Resource(_)
536            | ShapeKind::Service(_)
537            | ShapeKind::Unresolved => {}
538        }
539        self.with_lifetime.insert(id.clone(), false);
540        false
541    }
542
543    pub(crate) fn map_lifetimes(&mut self, model: &Model) {
544        // Initialize with any types that require lifetimes in their declarations
545        // TODO: remove DocumentRef here and use trait in smithy file instead
546        let document_ref = ShapeID::new_unchecked("org.wasmcloud.common", "DocumentRef", None);
547        self.with_lifetime.insert(document_ref, true);
548        for (id, traits, shape) in model.shapes().map(|s| (s.id(), s.traits(), s.body())) {
549            self.map_lifetime(model, id, traits, shape);
550        }
551    }
552
553    /// Checks whether the shape should have a lifetime annotation.
554    pub(crate) fn has_lifetime(&self, id: &ShapeID) -> bool {
555        *self.with_lifetime.get(id).unwrap_or(&false)
556    }
557
558    /// Apply documentation traits: (documentation, deprecated, unstable)
559    fn apply_documentation_traits(
560        &mut self,
561        w: &mut Writer,
562        id: &Identifier,
563        traits: &AppliedTraits,
564    ) {
565        if let Some(Some(Value::String(text))) =
566            traits.get(&prelude_shape_named(TRAIT_DOCUMENTATION).unwrap())
567        {
568            self.write_documentation(w, id, text);
569        }
570
571        // '@deprecated' trait
572        if let Some(Some(Value::Object(map))) =
573            traits.get(&prelude_shape_named(TRAIT_DEPRECATED).unwrap())
574        {
575            w.write(b"#[deprecated(");
576            if let Some(Value::String(since)) = map.get("since") {
577                writeln!(w, "since=\"{since}\"").unwrap();
578            }
579            if let Some(Value::String(message)) = map.get("message") {
580                writeln!(w, "note=\"{message}\"").unwrap();
581            }
582            w.write(b")\n");
583        }
584
585        // '@unstable' trait
586        if traits.get(&prelude_shape_named(TRAIT_UNSTABLE).unwrap()).is_some() {
587            self.write_comment(w, CommentKind::Documentation, "@unstable");
588        }
589    }
590
591    /// field type, wrapped with Option if field is not required
592    pub(crate) fn field_type_string(
593        &self,
594        field: &MemberShape,
595        lt: Lifetime<'_>,
596    ) -> Result<String> {
597        let target = field.target();
598        self.type_string(
599            if is_optional_type(field) {
600                Ty::Opt(target)
601            } else {
602                Ty::Shape(target)
603            },
604            lt,
605        )
606    }
607
608    /// Write a type name, a primitive or defined type, with or without deref('&') and with or without Option<>
609    /// The lifetime parameter will only be used if the type requires a lifetime
610    pub(crate) fn type_string(&self, ty: Ty<'_>, lt: Lifetime<'_>) -> Result<String> {
611        let mut s = String::new();
612        match ty {
613            Ty::Opt(id) => {
614                s.push_str("Option<");
615                s.push_str(&self.type_string(Ty::Shape(id), lt)?);
616                s.push('>');
617            }
618            Ty::Ref(id) => {
619                s.push('&');
620                s.push_str(&self.type_string(Ty::Shape(id), lt)?);
621            }
622            Ty::Ptr(_) => {
623                unreachable!() // invalid rust
624            }
625            Ty::Shape(id) => {
626                let name = id.shape_name().to_string();
627                if id.namespace() == prelude_namespace_id() {
628                    let ty = match name.as_ref() {
629                        SHAPE_BLOB => "Vec<u8>",
630                        SHAPE_BOOLEAN | SHAPE_PRIMITIVEBOOLEAN => "bool",
631                        SHAPE_STRING => "String",
632                        SHAPE_BYTE | SHAPE_PRIMITIVEBYTE => "i8",
633                        SHAPE_SHORT | SHAPE_PRIMITIVESHORT => "i16",
634                        SHAPE_INTEGER | SHAPE_PRIMITIVEINTEGER => "i32",
635                        SHAPE_LONG | SHAPE_PRIMITIVELONG => "i64",
636                        SHAPE_FLOAT | SHAPE_PRIMITIVEFLOAT => "f32",
637                        SHAPE_DOUBLE | SHAPE_PRIMITIVEDOUBLE => "f64",
638                        SHAPE_DOCUMENT => {
639                            s.push_str(&self.import_core);
640                            "::common::Document"
641                        }
642                        SHAPE_TIMESTAMP => "Timestamp",
643                        SHAPE_BIGINTEGER => {
644                            cfg_if::cfg_if! {
645                                if #[cfg(feature = "BigInteger")] { "BigInteger" } else { return Err(Error::UnsupportedBigInteger) }
646                            }
647                        }
648                        SHAPE_BIGDECIMAL => {
649                            cfg_if::cfg_if! {
650                                if #[cfg(feature = "BigDecimal")] { "BigDecimal" } else { return Err(Error::UnsupportedBigDecimal) }
651                            }
652                        }
653                        _ => return Err(Error::UnsupportedType(name)),
654                    };
655                    s.push_str(ty);
656                } else if id.namespace() == wasmcloud_model_namespace() {
657                    match name.as_str() {
658                        "U64" | "U32" | "U16" | "U8" => {
659                            s.push('u');
660                            s.push_str(&name[1..])
661                        }
662                        "I64" | "I32" | "I16" | "I8" => {
663                            s.push('i');
664                            s.push_str(&name[1..]);
665                        }
666                        "F64" => s.push_str("f64"),
667                        "F32" => s.push_str("f32"),
668                        _ => {
669                            if self.namespace.is_none()
670                                || self.namespace.as_ref().unwrap() != id.namespace()
671                            {
672                                s.push_str(&self.import_core);
673                                s.push_str("::model::");
674                            }
675                            s.push_str(&self.to_type_name_case(&name));
676                        }
677                    };
678                } else if self.namespace.is_some()
679                    && id.namespace() == self.namespace.as_ref().unwrap()
680                {
681                    // we are in the same namespace so we don't need to specify namespace
682                    s.push_str(&self.to_type_name_case(&id.shape_name().to_string()));
683                    if self.has_lifetime(id) {
684                        s.push_str(&lt.annotate());
685                    }
686                } else {
687                    s.push_str(&self.get_crate_path(id)?);
688                    s.push_str(&self.to_type_name_case(&id.shape_name().to_string()));
689                    if self.has_lifetime(id) {
690                        s.push_str(&lt.annotate());
691                    }
692                }
693            }
694        }
695        Ok(s)
696    }
697
698    /// Write a type name, a primitive or defined type, with or without deref('&') and with or without Option<>
699    fn write_type(&mut self, w: &mut Writer, ty: Ty<'_>, lt: Lifetime<'_>) -> Result<()> {
700        w.write(&self.type_string(ty, lt)?);
701        Ok(())
702    }
703
704    // declaration for simple type
705    fn declare_simple_shape(
706        &mut self,
707        w: &mut Writer,
708        id: &Identifier,
709        traits: &AppliedTraits,
710        simple: &Simple,
711        lt: Lifetime,
712    ) -> Result<()> {
713        self.apply_documentation_traits(w, id, traits);
714        w.write(b"pub type ");
715        self.write_ident(w, id);
716        if lt.is_some() {
717            w.write(lt.annotate().as_bytes());
718        }
719        w.write(b" = ");
720        let ty = match simple {
721            Simple::Blob => "Vec<u8>",
722            Simple::Boolean => "bool",
723            Simple::String => "String",
724            Simple::Byte => "i8",
725            Simple::Short => "i16",
726            Simple::Integer => "i32",
727            Simple::Long => "i64",
728            Simple::Float => "f32",
729            Simple::Double => "f64",
730            Simple::Document => {
731                w.write(&self.import_core);
732                "::common::Document"
733            }
734            Simple::Timestamp => "Timestamp",
735            Simple::BigInteger => {
736                cfg_if::cfg_if! {
737                    if #[cfg(feature = "BigInteger")] { "BigInteger" } else { return Err(Error::UnsupportedBigInteger) }
738                }
739            }
740            Simple::BigDecimal => {
741                cfg_if::cfg_if! {
742                    if #[cfg(feature = "BigDecimal")] { "BigDecimal" } else { return Err(Error::UnsupportedBigDecimal) }
743                }
744            }
745        };
746        w.write(ty);
747        //let end_mark = w.pos();
748        //self.type_aliases
749        //    .insert(id.to_string(), w.get_slice(start_pos, end_pos).to_string());
750        w.write(b";\n\n");
751        Ok(())
752    }
753
754    fn declare_map_shape(
755        &mut self,
756        w: &mut Writer,
757        id: &Identifier,
758        traits: &AppliedTraits,
759        shape: &MapShape,
760        lt: Lifetime<'_>,
761    ) -> Result<()> {
762        self.apply_documentation_traits(w, id, traits);
763        w.write(b"pub type ");
764        self.write_ident(w, id);
765        if lt.is_some() {
766            w.write(lt.annotate().as_bytes());
767        }
768        w.write(b" = ");
769        w.write(DEFAULT_MAP_TYPE);
770        w.write(b"<");
771        self.write_type(w, Ty::Shape(shape.key().target()), lt)?;
772        w.write(b",");
773        self.write_type(w, Ty::Shape(shape.value().target()), lt)?;
774        w.write(b">;\n\n");
775        Ok(())
776    }
777
778    fn declare_list_or_set_shape(
779        &mut self,
780        w: &mut Writer,
781        id: &Identifier,
782        traits: &AppliedTraits,
783        shape: &ListOrSet,
784        typ: &str,
785        lt: Lifetime<'_>,
786    ) -> Result<()> {
787        self.apply_documentation_traits(w, id, traits);
788        w.write(b"pub type ");
789        self.write_ident(w, id);
790        if lt.is_some() {
791            w.write(lt.annotate().as_bytes());
792        }
793        w.write(b" = ");
794        w.write(typ);
795        w.write(b"<");
796        self.write_type(w, Ty::Shape(shape.member().target()), lt)?;
797        w.write(b">;\n\n");
798        Ok(())
799    }
800
801    fn declare_structure_shape(
802        &mut self,
803        w: &mut Writer,
804        id: &ShapeID,
805        traits: &AppliedTraits,
806        strukt: &StructureOrUnion,
807        lt: Lifetime<'_>,
808    ) -> Result<()> {
809        let is_trait_struct = traits.contains_key(&prelude_shape_named(TRAIT_TRAIT).unwrap());
810        let ident = id.shape_name();
811        self.apply_documentation_traits(w, ident, traits);
812        let preface = self.build_preface(id, traits);
813        w.write(&preface.derives());
814        if preface.non_exhaustive {
815            w.write(b"#[non_exhaustive]\n");
816        }
817        w.write(b"pub struct ");
818        self.write_ident(w, ident);
819        w.write(&lt.annotate());
820        w.write(b" {\n");
821        let (fields, _is_numbered) = get_sorted_fields(ident, strukt)?;
822        for member in fields.iter() {
823            self.apply_documentation_traits(w, member.id(), member.traits());
824            // use the declared name for serialization, unless an override is declared
825            // with `@sesrialization(name: SNAME)`
826            let (field_name, ser_name) = self.get_field_name_and_ser_name(member)?;
827            if ser_name != field_name {
828                writeln!(w, "  #[serde(rename=\"{ser_name}\")] ").unwrap();
829            }
830            let lt = if self.has_lifetime(member.target()) { lt } else { Lifetime::None };
831
832            // for rmp-msgpack - need to use serde_bytes serializer for Blob (and Option<Blob>)
833            // otherwise Vec<u8> is written as an array of individual bytes, not a memory slice.
834            //
835            // We should only add this serde declaration if the struct is tagged with @wasmbusData.
836            // Because of the possibility of smithy models being used for messages
837            // that don't use wasmbus protocols, we don't want to "automatically"
838            // assume wasmbusData trait, even if we are compiled with (feature="wasmbus").
839            //
840            // However, we don't really need to require users to declare
841            // structs with wasmbusData - we can infer it if it's used in an operation
842            // for a service tagged with wasmbus. This would require traversing the model
843            // from all services tagged with wasmbus, looking at the inputs and outputs
844            // of all operations for those services, and, transitively, any
845            // data types referred from them, including struct fields, list members,
846            // and map keys and values.
847            // Until that traversal is implemented, assume that wasmbusData is enabled
848            // for everything. This saves developers from needing to add a wasmbusData
849            // declaration on every struct, which is error-prone.
850            // I can't think of a use case when adding serde_bytes is the wrong thing to do,
851            // even if msgpack is not used for serialization, so it seems like
852            // an acceptable simplification.
853            #[cfg(feature = "wasmbus")]
854            if member.target() == &ShapeID::new_unchecked("smithy.api", "Blob", None) {
855                w.write(r#"  #[serde(with="serde_bytes")] "#);
856            }
857
858            if is_optional_type(member) {
859                w.write(r#"  #[serde(default, skip_serializing_if = "Option::is_none")] "#);
860            } else if (is_trait_struct && !member.is_required())
861                || has_default(self.model.unwrap(), member)
862                || (member.target()
863                    == &ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_DOCUMENT, None))
864            {
865                // trait structs are deserialized only and need default values
866                // on deserialization, so always add [serde(default)] for trait structs.
867
868                // Additionally, add [serde(default)] for types that have a natural
869                // default value. Although not required if both ends of the protocol
870                // are implemented correctly, it may improve message resiliency
871                // if we can accept structs with missing fields, if the fields
872                // can be filled in/constructed with appropriate default values.
873                // This only applies if the default is a zero, empty list/map, etc,
874                // and we don't make any attempt to determine if a user-declared
875                // struct has a zero default.
876                // See the comment for has_default for more info.
877                w.write(r#"  #[serde(default)] "#);
878            }
879            w.write(
880                format!(
881                    "  pub {}: {},\n",
882                    &field_name,
883                    self.field_type_string(member, lt)?
884                )
885                .as_bytes(),
886            );
887        }
888        w.write(b"}\n\n");
889        Ok(())
890    }
891
892    fn declare_union_shape(
893        &mut self,
894        w: &mut Writer,
895        id: &ShapeID,
896        traits: &AppliedTraits,
897        strukt: &StructureOrUnion,
898        lt: Lifetime<'_>,
899    ) -> Result<()> {
900        let ident = id.shape_name();
901        let (fields, is_numbered) = get_sorted_fields(ident, strukt)?;
902        if !is_numbered {
903            return Err(Error::Model(format!(
904                "union {ident} must have numbered fields"
905            )));
906        }
907        self.apply_documentation_traits(w, ident, traits);
908        let mut preface = self.build_preface(id, traits);
909        preface.default = false;
910        w.write(&preface.derives());
911        if preface.non_exhaustive {
912            w.write(b"#[non_exhaustive]\n");
913        }
914        w.write(b"pub enum ");
915        self.write_ident(w, ident);
916        if lt.is_some() {
917            w.write(lt.annotate().as_bytes());
918        }
919        w.write(b" {\n");
920        for member in fields.iter() {
921            self.apply_documentation_traits(w, member.id(), member.traits());
922            let field_num = member.field_num().unwrap();
923            writeln!(w, "/// n({field_num})").unwrap();
924            let variant_name = self.to_type_name_case(&member.id().to_string());
925            if member.target() == crate::model::unit_shape() {
926                writeln!(w, "{variant_name},\n").unwrap();
927            } else {
928                writeln!(
929                    w,
930                    "{}({}),",
931                    variant_name,
932                    self.type_string(Ty::Shape(member.target()), lt)?
933                )
934                .unwrap();
935            }
936        }
937        w.write(b"}\n\n");
938        Ok(())
939    }
940
941    /// Declares the service as a rust Trait whose methods are the smithy service operations
942    fn write_service_interface(
943        &mut self,
944        w: &mut Writer,
945        model: &Model,
946        service: &ServiceInfo,
947    ) -> Result<()> {
948        self.apply_documentation_traits(w, service.id, service.traits);
949
950        #[cfg(feature = "wasmbus")]
951        self.add_wasmbus_comments(w, service)?;
952
953        w.write(b"#[async_trait]\npub trait ");
954        self.write_ident(w, service.id);
955        w.write(b"{\n");
956        self.write_service_contract_getter(w, service)?;
957
958        for operation in service.service.operations() {
959            // if operation is not declared in this namespace, don't define it here
960            if let Some(ref ns) = self.namespace {
961                if operation.namespace() != ns {
962                    continue;
963                }
964            }
965            let (op, op_traits) = get_operation(model, operation, service.id)?;
966            let method_id = operation.shape_name();
967            let _flags = self.write_method_signature(w, method_id, op_traits, op)?;
968            w.write(b";\n");
969        }
970        w.write(b"}\n\n");
971        Ok(())
972    }
973
974    /// add getter for capability contract id
975    fn write_service_contract_getter(
976        &mut self,
977        w: &mut Writer,
978        service: &ServiceInfo,
979    ) -> Result<()> {
980        if let Some(contract_id) = service.wasmbus_contract_id() {
981            writeln!(
982                w,
983                r#"
984                /// returns the capability contract id for this interface
985                fn contract_id() -> &'static str {{ "{contract_id}" }}"#
986            )
987            .unwrap();
988        }
989        Ok(())
990    }
991
992    #[cfg(feature = "wasmbus")]
993    fn add_wasmbus_comments(&mut self, w: &mut Writer, service: &ServiceInfo) -> Result<()> {
994        // currently the only thing we do with Wasmbus in codegen is add comments
995        let wasmbus: Option<Wasmbus> = get_trait(service.traits, crate::model::wasmbus_trait())?;
996        if let Some(wasmbus) = wasmbus {
997            if let Some(contract_id) = service.wasmbus_contract_id() {
998                let text = format!("wasmbus.contractId: {contract_id}");
999                self.write_documentation(w, service.id, &text);
1000            }
1001            if wasmbus.provider_receive {
1002                let text = "wasmbus.providerReceive";
1003                self.write_documentation(w, service.id, text);
1004            }
1005            if wasmbus.actor_receive {
1006                let text = "wasmbus.actorReceive";
1007                self.write_documentation(w, service.id, text);
1008            }
1009        }
1010        Ok(())
1011    }
1012
1013    /// write trait function declaration "async fn method(args) -> Result< return_type, RpcError >"
1014    /// does not write trailing semicolon so this can be used for declaration and implementation
1015    fn write_method_signature(
1016        &mut self,
1017        w: &mut Writer,
1018        method_id: &Identifier,
1019        method_traits: &AppliedTraits,
1020        op: &Operation,
1021    ) -> Result<MethodArgFlags> {
1022        let method_name = self.to_method_name(method_id, method_traits);
1023        let mut arg_flags = MethodArgFlags::Normal;
1024        self.apply_documentation_traits(w, method_id, method_traits);
1025        let input_lt = if let Some(input_type) = op.input() {
1026            self.has_lifetime(input_type)
1027        } else {
1028            false
1029        };
1030        let output_lt = if let Some(output_type) = op.output() {
1031            self.has_lifetime(output_type)
1032        } else {
1033            false
1034        };
1035        let func_lt = if input_lt { "'vin," } else { "" };
1036        w.write(b"async fn ");
1037        w.write(&method_name);
1038        if let Some(input_type) = op.input() {
1039            if input_type == &ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_STRING, None) {
1040                arg_flags = MethodArgFlags::ToString;
1041                write!(w, "<{func_lt}TS:ToString + ?Sized + std::marker::Sync>").unwrap();
1042            } else if input_lt {
1043                write!(w, "<{func_lt}>").unwrap();
1044            }
1045        }
1046        w.write(b"(&self, ctx: &Context");
1047        if let Some(input_type) = op.input() {
1048            w.write(b", arg: "); // pass arg by reference
1049            if matches!(arg_flags, MethodArgFlags::ToString) {
1050                w.write(b"&TS");
1051            } else {
1052                self.write_type(
1053                    w,
1054                    Ty::Ref(input_type),
1055                    if input_lt { Lifetime::L("vin") } else { Lifetime::None },
1056                )?;
1057            }
1058        }
1059        w.write(b") -> RpcResult<");
1060        if let Some(output_type) = op.output() {
1061            self.write_type(
1062                w,
1063                Ty::Shape(output_type),
1064                if output_lt { Lifetime::L("static") } else { Lifetime::None },
1065            )?;
1066        } else {
1067            w.write(b"()");
1068        }
1069        w.write(b">");
1070        Ok(arg_flags)
1071    }
1072
1073    // pub trait FooReceiver : MessageDispatch + Foo { ... }
1074    fn write_service_receiver(
1075        &mut self,
1076        w: &mut Writer,
1077        model: &Model,
1078        service: &ServiceInfo,
1079    ) -> Result<()> {
1080        let doc = format!(
1081            "{}Receiver receives messages defined in the {} service trait",
1082            service.id, service.id
1083        );
1084        self.write_comment(w, CommentKind::Documentation, &doc);
1085        self.apply_documentation_traits(w, service.id, service.traits);
1086        w.write(b"#[doc(hidden)]\n#[async_trait]\npub trait ");
1087        self.write_ident_with_suffix(w, service.id, "Receiver")?;
1088        w.write(b" : MessageDispatch + ");
1089        self.write_ident(w, service.id);
1090        let proto = crate::model::wasmbus_proto(service.traits)?;
1091        let has_cbor = proto.map(|pv| pv.has_cbor()).unwrap_or(false);
1092        w.write(
1093            br#"{
1094            async fn dispatch(&self, ctx:&Context, message:Message<'_>) -> Result<Vec<u8>, RpcError> {
1095                match message.method {
1096        "#,
1097        );
1098
1099        for method_id in service.service.operations() {
1100            // we don't add operations defined in another namespace
1101            if let Some(ref ns) = self.namespace {
1102                if method_id.namespace() != ns {
1103                    continue;
1104                }
1105            }
1106            let method_ident = method_id.shape_name();
1107            let (op, method_traits) = get_operation(model, method_id, service.id)?;
1108            w.write(b"\"");
1109            w.write(&self.op_dispatch_name(method_ident));
1110            w.write(b"\" => {\n");
1111            if let Some(op_input) = op.input() {
1112                let borrowed = self.has_lifetime(op_input);
1113                let symbol = op_input.shape_name().to_string();
1114                if has_cbor {
1115                    let crate_prefix = self.get_crate_path(op_input)?;
1116                    writeln!(
1117                        w,
1118                        r#"
1119                    let value : {} = {}::common::{}(&message.arg, &{}decode_{})
1120                      .map_err(|e| RpcError::Deser(format!("'{}': {{}}", e)))?;"#,
1121                        self.type_string(Ty::Shape(op_input), Lifetime::Any)?,
1122                        self.import_core,
1123                        if borrowed { "decode_borrowed" } else { "decode" },
1124                        &crate_prefix,
1125                        crate::strings::to_snake_case(&symbol),
1126                        &symbol,
1127                    )
1128                    .unwrap()
1129                } else {
1130                    writeln!(
1131                        w,
1132                        r#"
1133                        let value: {} = {}::common::deserialize(&message.arg)
1134                      .map_err(|e| RpcError::Deser(format!("'{}': {{}}", e)))?;
1135                        "#,
1136                        self.type_string(Ty::Shape(op_input), Lifetime::Any)?,
1137                        self.import_core,
1138                        &symbol,
1139                    )
1140                    .unwrap()
1141                }
1142            }
1143            // let resp = Trait::method(self, ctx, &value).await?;
1144            if op.output().is_some() {
1145                w.write(b"let resp = ");
1146            } else {
1147                w.write(b"let _resp = ");
1148            }
1149            let method_name = self.to_method_name(method_ident, method_traits);
1150            self.write_ident(w, service.id); // Service::method
1151            w.write(b"::");
1152            w.write(&method_name);
1153            w.write(b"(self, ctx");
1154            if op.has_input() {
1155                w.write(b", &value");
1156            }
1157            w.write(b").await?;\n");
1158
1159            if let Some(_op_output) = op.output() {
1160                // serialize result
1161                if has_cbor {
1162                    writeln!(
1163                        w,
1164                        "let mut e = {}::cbor::vec_encoder(true);",
1165                        &self.import_core
1166                    )
1167                    .unwrap();
1168                    let s = self.encode_shape_id(
1169                        _op_output,
1170                        crate::encode_rust::ValExpr::Plain("resp"),
1171                        true,
1172                    )?;
1173                    w.write(&s);
1174                    w.write(b"let buf = e.into_inner();\n");
1175                } else {
1176                    writeln!(
1177                        w,
1178                        "let buf = {}::common::serialize(&resp)?;\n",
1179                        &self.import_core
1180                    )
1181                    .unwrap();
1182                }
1183            } else {
1184                w.write(b"let buf = Vec::new();\n");
1185            }
1186            w.write(b"Ok(buf)\n}\n");
1187        }
1188        w.write(b"_ => Err(RpcError::MethodNotHandled(format!(\"");
1189        self.write_ident(w, service.id);
1190        w.write(b"::{}\", message.method))),\n");
1191        w.write(b"}\n}\n}\n\n"); // end match, end fn dispatch, end trait
1192
1193        Ok(())
1194    }
1195
1196    /// writes the service sender struct and constructor
1197    // pub struct FooSender{ ... }
1198    fn write_service_sender(
1199        &mut self,
1200        w: &mut Writer,
1201        model: &Model,
1202        service: &ServiceInfo,
1203    ) -> Result<()> {
1204        let doc = format!(
1205            "{}Sender sends messages to a {} service",
1206            service.id, service.id
1207        );
1208        self.write_comment(w, CommentKind::Documentation, &doc);
1209        self.apply_documentation_traits(w, service.id, service.traits);
1210        let proto = crate::model::wasmbus_proto(service.traits)?;
1211        let has_cbor = proto.map(|pv| pv.has_cbor()).unwrap_or(false);
1212        writeln!(
1213            w,
1214            r#"/// client for sending {} messages
1215              #[derive(Clone, Debug)]
1216              pub struct {}Sender<T:Transport>  {{ transport: T }}
1217
1218              impl<T:Transport> {}Sender<T> {{
1219                  /// Constructs a {}Sender with the specified transport
1220                  pub fn via(transport: T) -> Self {{
1221                      Self{{ transport }}
1222                  }}
1223                  
1224                  pub fn set_timeout(&self, interval: std::time::Duration) {{
1225                    self.transport.set_timeout(interval);
1226                  }}
1227              }}"#,
1228            service.id, service.id, service.id, service.id,
1229        )
1230        .unwrap();
1231        #[cfg(feature = "wasmbus")]
1232        w.write(&self.actor_receive_sender_constructors(service.id, service.traits)?);
1233        #[cfg(feature = "wasmbus")]
1234        w.write(&self.provider_receive_sender_constructors(service.id, service.traits)?);
1235
1236        // implement Trait for TraitSender
1237        w.write(b"#[async_trait]\nimpl<T:Transport + std::marker::Sync + std::marker::Send> ");
1238        self.write_ident(w, service.id);
1239        w.write(b" for ");
1240        self.write_ident_with_suffix(w, service.id, "Sender")?;
1241        w.write(b"<T> {\n");
1242
1243        for method_id in service.service.operations() {
1244            // we don't add operations defined in another namespace
1245            if let Some(ref ns) = self.namespace {
1246                if method_id.namespace() != ns {
1247                    continue;
1248                }
1249            }
1250            let method_ident = method_id.shape_name();
1251
1252            let (op, method_traits) = get_operation(model, method_id, service.id)?;
1253            w.write(b"#[allow(unused)]\n");
1254            let arg_flags = self.write_method_signature(w, method_ident, method_traits, op)?;
1255            let _arg_is_string = matches!(arg_flags, MethodArgFlags::ToString);
1256            w.write(b" {\n");
1257            if let Some(_op_input) = op.input() {
1258                if has_cbor {
1259                    if _arg_is_string {
1260                        w.write(b"let arg = arg.to_string();\n");
1261                    }
1262                    writeln!(
1263                        w,
1264                        "let mut e = {}::cbor::vec_encoder(true);",
1265                        &self.import_core
1266                    )
1267                    .unwrap();
1268                    let s = self.encode_shape_id(
1269                        _op_input,
1270                        if _arg_is_string {
1271                            crate::encode_rust::ValExpr::Ref("arg.as_ref()")
1272                        } else {
1273                            crate::encode_rust::ValExpr::Ref("arg")
1274                        },
1275                        true,
1276                    )?;
1277                    w.write(&s);
1278                    w.write(b"let buf = e.into_inner(); \n");
1279                    //let tn = crate::strings::to_snake_case(&self.type_string(Ty::Shape(op.input().as_ref().unwrap()))?);
1280                    //w.write(&format!("encode_{}(&mut e, arg)?;", tn));
1281                } else if matches!(arg_flags, MethodArgFlags::ToString) {
1282                    writeln!(
1283                        w,
1284                        "let buf = {}::common::serialize(&arg.to_string())?;\n",
1285                        &self.import_core
1286                    )
1287                    .unwrap();
1288                } else {
1289                    writeln!(
1290                        w,
1291                        "let buf = {}::common::serialize(arg)?;\n",
1292                        &self.import_core
1293                    )
1294                    .unwrap();
1295                }
1296            } else {
1297                w.write(b"let buf = *b\"\";\n");
1298            }
1299            w.write(b"let resp = self.transport.send(ctx, Message{ method: ");
1300            // note: legacy is just the latter part
1301            w.write(b"\"");
1302            w.write(&self.full_dispatch_name(service.id, method_ident));
1303            //w.write(&self.op_dispatch_name(method_ident));
1304            w.write(b"\", arg: Cow::Borrowed(&buf)}, None).await?;\n");
1305            if let Some(op_output) = op.output() {
1306                let symbol = op_output.shape_name().to_string();
1307                if has_cbor {
1308                    let crate_prefix = self.get_crate_path(op_output)?;
1309                    write!(
1310                        w,
1311                        r#"
1312                    let value : {} = {}::common::{}(&resp, &{}decode_{})
1313                        .map_err(|e| RpcError::Deser(format!("'{{}}': {}", e)))?;
1314                    Ok(value)
1315                    "#,
1316                        self.type_string(Ty::Shape(op_output), Lifetime::L("static"))?,
1317                        self.import_core,
1318                        if self.has_lifetime(op_output) { "decode_owned" } else { "decode" },
1319                        &crate_prefix,
1320                        crate::strings::to_snake_case(&symbol),
1321                        &symbol,
1322                    )
1323                    .unwrap();
1324                } else {
1325                    write!(
1326                        w,
1327                        r#"
1328                    let value : {} = {}::common::deserialize(&resp)
1329                        .map_err(|e| RpcError::Deser(format!("'{{}}': {}", e)))?;
1330                    Ok(value)
1331                    "#,
1332                        self.type_string(Ty::Shape(op_output), Lifetime::Any)?,
1333                        self.import_core,
1334                        &symbol,
1335                    )
1336                    .unwrap();
1337                }
1338            } else {
1339                w.write(b"Ok(())");
1340            }
1341            w.write(b" }\n");
1342        }
1343        w.write(b"}\n\n");
1344        Ok(())
1345    }
1346
1347    /// add sender constructors for calling actors, for services that declare actorReceive
1348    #[cfg(feature = "wasmbus")]
1349    fn actor_receive_sender_constructors(
1350        &mut self,
1351        service_id: &Identifier,
1352        service_traits: &AppliedTraits,
1353    ) -> Result<String> {
1354        let ctors = if let Some(Wasmbus { actor_receive: true, .. }) =
1355            get_trait(service_traits, crate::model::wasmbus_trait())?
1356        {
1357            format!(
1358                r#"
1359                #[cfg(not(target_arch="wasm32"))]
1360                impl<'send> {}Sender<{}::provider::ProviderTransport<'send>> {{
1361                    /// Constructs a Sender using an actor's LinkDefinition,
1362                    /// Uses the provider's HostBridge for rpc
1363                    pub fn for_actor(ld: &'send {}::core::LinkDefinition) -> Self {{
1364                        Self{{ transport: {}::provider::ProviderTransport::new(ld,None) }}
1365                    }}
1366                }}
1367                #[cfg(target_arch = "wasm32")]
1368                impl {}Sender<{}::actor::prelude::WasmHost> {{
1369                    /// Constructs a client for actor-to-actor messaging
1370                    /// using the recipient actor's public key
1371                    pub fn to_actor(actor_id: &str) -> Self {{
1372                        let transport = {}::actor::prelude::WasmHost::to_actor(actor_id.to_string()).unwrap();
1373                        Self{{ transport }}
1374                    }}
1375
1376                }}
1377                "#,
1378                // for_actor() (from provider)
1379                service_id,
1380                &self.import_core,
1381                &self.import_core,
1382                &self.import_core,
1383                // impl declaration
1384                service_id,
1385                &self.import_core,
1386                // to_actor() (from actor)
1387                &self.import_core,
1388            )
1389        } else {
1390            String::new()
1391        };
1392        Ok(ctors)
1393    }
1394
1395    /// add sender constructors for actors calling providers
1396    /// This is only used for wasm32 targets and for services that declare 'providerReceive'
1397    #[cfg(feature = "wasmbus")]
1398    fn provider_receive_sender_constructors(
1399        &mut self,
1400        service_id: &Identifier,
1401        service_traits: &AppliedTraits,
1402    ) -> Result<String> {
1403        let ctors = if let Some(Wasmbus {
1404            provider_receive: true,
1405            contract_id: Some(contract),
1406            ..
1407        }) = get_trait(service_traits, crate::model::wasmbus_trait())?
1408        {
1409            format!(
1410                r#"
1411                #[cfg(target_arch = "wasm32")]
1412                impl {}Sender<{}::actor::prelude::WasmHost> {{
1413
1414                    /// Constructs a client for sending to a {} provider
1415                    /// implementing the '{}' capability contract, with the "default" link
1416                    pub fn new() -> Self {{
1417                        let transport = {}::actor::prelude::WasmHost::to_provider("{}", "default").unwrap();
1418                        Self {{ transport }}
1419                    }}
1420
1421                    /// Constructs a client for sending to a {} provider
1422                    /// implementing the '{}' capability contract, with the specified link name
1423                    pub fn new_with_link(link_name: &str) -> {}::error::RpcResult<Self> {{
1424                        let transport =  {}::actor::prelude::WasmHost::to_provider("{}", link_name)?;
1425                        Ok(Self {{ transport }})
1426                    }}
1427
1428                }}
1429                "#,
1430                // impl declaration
1431                service_id,
1432                &self.import_core,
1433                // new() (provider)
1434                service_id,
1435                contract,
1436                &self.import_core,
1437                contract,
1438                // new_with_link()
1439                service_id,
1440                contract,
1441                &self.import_core,
1442                &self.import_core,
1443                contract,
1444            )
1445        } else {
1446            String::new()
1447        };
1448        Ok(ctors)
1449    }
1450
1451    /// returns the Rust package prefix for the symbol, using metadata crate declarations
1452    pub(crate) fn get_crate_path(&self, id: &ShapeID) -> Result<String> {
1453        let namespace = id.namespace();
1454        // special case for Document
1455        if id == &ShapeID::new_unchecked(PRELUDE_NAMESPACE, "Document", None) {
1456            return Ok("wasmbus_rpc::common::".to_string());
1457        }
1458
1459        // no prefix required for prelude (smithy.api) or wasmbus_model,
1460        // because they are always imported
1461        if namespace == prelude_namespace_id()
1462            || namespace == wasmcloud_model_namespace()
1463            // no prefix required if namespace is the namespace of
1464            // the file we are generating
1465            || (self.namespace.is_some()
1466            && namespace == self.namespace.as_ref().unwrap())
1467        {
1468            return Ok(String::new());
1469        }
1470
1471        // look up the crate name
1472        // the crate name should be valid rust syntax. If not, they'll get an error with rustc
1473        match self.packages.get(&namespace.to_string()) {
1474            Some(crate::model::PackageName { crate_name: Some(crate_name), .. }) => {
1475                Ok(format!("{crate_name}::"))
1476            }
1477            _ => Err(Error::Model(format!(
1478                "undefined crate for namespace '{}' symbol '{}'. Make sure codegen.toml includes \
1479                 all dependent namespaces, and that the dependent .smithy file contains package \
1480                 metadata with crate: value",
1481                namespace,
1482                id.shape_name(),
1483            ))),
1484        }
1485    }
1486
1487    /// returns crate prefix for model namespace
1488    pub(crate) fn get_model_crate(&self) -> String {
1489        if self.namespace.is_none()
1490            || self.namespace.as_ref().unwrap() != wasmcloud_model_namespace()
1491        {
1492            format!("{}::model::", &self.import_core)
1493        } else {
1494            String::new()
1495        }
1496    }
1497
1498    fn build_preface(&self, id: &ShapeID, traits: &AppliedTraits) -> StructPreface {
1499        let mut preface = StructPreface::default();
1500        if self.has_lifetime(id) {
1501            preface.deserialize = false;
1502        }
1503        // derive(Default) is disabled for traits and enabled for all other structs
1504        let is_trait_struct = traits.contains_key(&prelude_shape_named(TRAIT_TRAIT).unwrap());
1505        if is_trait_struct {
1506            preface.default = false
1507        }
1508
1509        if let Ok(Some(cg)) = get_trait::<CodegenRust>(traits, codegen_rust_trait()) {
1510            preface.default = !cg.no_derive_default;
1511            preface.eq = !cg.no_derive_eq;
1512            preface.non_exhaustive = cg.non_exhaustive;
1513        }
1514        preface
1515    }
1516} // impl CodeGenRust
1517
1518/// flags used to build `#[derive(...)]` and `#[non_exhaustive]` annotations for struct and enum
1519struct StructPreface {
1520    clone: bool,
1521    copy: bool,
1522    debug: bool,
1523    default: bool,
1524    eq: bool,
1525    partial_eq: bool,
1526    serialize: bool,
1527    deserialize: bool,
1528    non_exhaustive: bool,
1529}
1530
1531impl Default for StructPreface {
1532    fn default() -> Self {
1533        StructPreface {
1534            clone: true,
1535            copy: false,
1536            debug: true,
1537            default: true,
1538            eq: true,
1539            partial_eq: true,
1540            serialize: true,
1541            deserialize: true,
1542            non_exhaustive: false,
1543        }
1544    }
1545}
1546impl StructPreface {
1547    /// Return `#[derive(...)]` attribute for struct or enum
1548    fn derives(&self) -> String {
1549        if self.clone
1550            || self.copy
1551            || self.debug
1552            || self.default
1553            || self.eq
1554            || self.partial_eq
1555            || self.serialize
1556            || self.deserialize
1557        {
1558            let mut s = "#[derive(".to_string();
1559            if self.clone {
1560                s.push_str("Clone,");
1561            }
1562            if self.copy {
1563                s.push_str("Copy,");
1564            }
1565            if self.debug {
1566                s.push_str("Debug,");
1567            }
1568            if self.default {
1569                s.push_str("Default,");
1570            }
1571            if self.deserialize {
1572                s.push_str("Deserialize,");
1573            }
1574            if self.eq {
1575                s.push_str("Eq,");
1576            }
1577            if self.partial_eq {
1578                s.push_str("PartialEq,");
1579            }
1580            if self.serialize {
1581                s.push_str("Serialize,");
1582            }
1583            s.push_str(")]\n");
1584            s
1585        } else {
1586            String::new()
1587        }
1588    }
1589}
1590
1591/// is_optional_type determines whether the field should be wrapped in Option<>
1592/// the value is true if it has an explicit `box` trait, or if it's
1593/// un-annotated and not one of (boolean, byte, short, integer, long, float, double)
1594pub(crate) fn is_optional_type(field: &MemberShape) -> bool {
1595    field.is_boxed()
1596        || (!field.is_required()
1597            && ![
1598                "Boolean", "Byte", "Short", "Integer", "Long", "Float", "Double",
1599            ]
1600            .contains(&field.target().shape_name().to_string().as_str()))
1601}
1602
1603/*
1604Opt   @required   @box    bool/int/...
16051     0           0       0
16060     0           0       1
16071     0           1       0
16081     0           1       1
16090     1           0       0
16100     1           0       1
1611x     1           1       0
1612x     1           1       1
1613*/
1614
1615// check that the codegen package has a parseable version
1616#[test]
1617fn package_semver() {
1618    let package_version = env!("CARGO_PKG_VERSION");
1619    let version = semver::Version::parse(package_version);
1620    assert!(
1621        version.is_ok(),
1622        "package version {package_version} has unexpected format"
1623    );
1624}
1625
1626/// Format rust source using rustfmt
1627pub struct RustSourceFormatter {
1628    /// either 'rustfmt', (the default, assumes ~/.cargo/bin is in your path,
1629    /// or a path to an executable
1630    program: String,
1631    /// any additional args
1632    args: Vec<String>,
1633}
1634
1635impl Default for RustSourceFormatter {
1636    fn default() -> Self {
1637        RustSourceFormatter {
1638            program: "rustfmt".into(),
1639            args: vec!["--edition".into(), "2021".into()],
1640        }
1641    }
1642}
1643
1644impl SourceFormatter for RustSourceFormatter {
1645    fn run(&self, source_files: &[&str]) -> Result<()> {
1646        let mut args: Vec<&str> = self.args.iter().map(|s| s.as_str()).collect();
1647        args.extend(source_files.iter());
1648        format::run_command(&self.program, &args)?;
1649        Ok(())
1650    }
1651}