weld_codegen/
codegen_go.rs

1//! Go language code-generator
2//!
3use std::{collections::HashMap, fmt, fmt::Write as _, path::Path, str::FromStr, string::ToString};
4
5use atelier_core::{
6    model::{
7        shapes::{
8            AppliedTraits, HasTraits, ListOrSet, Map as MapShape, MemberShape, Operation, Service,
9            ShapeKind, Simple, StructureOrUnion,
10        },
11        values::Value,
12        HasIdentity, Identifier, Model, NamespaceID, ShapeID,
13    },
14    prelude::{
15        prelude_namespace_id, prelude_shape_named, PRELUDE_NAMESPACE, SHAPE_BIGDECIMAL,
16        SHAPE_BIGINTEGER, SHAPE_BLOB, SHAPE_BOOLEAN, SHAPE_BYTE, SHAPE_DOCUMENT, SHAPE_DOUBLE,
17        SHAPE_FLOAT, SHAPE_INTEGER, SHAPE_LONG, SHAPE_PRIMITIVEBOOLEAN, SHAPE_PRIMITIVEBYTE,
18        SHAPE_PRIMITIVEDOUBLE, SHAPE_PRIMITIVEFLOAT, SHAPE_PRIMITIVEINTEGER, SHAPE_PRIMITIVELONG,
19        SHAPE_PRIMITIVESHORT, SHAPE_SHORT, SHAPE_STRING, SHAPE_TIMESTAMP, TRAIT_DEPRECATED,
20        TRAIT_DOCUMENTATION, TRAIT_TRAIT, TRAIT_UNSTABLE,
21    },
22};
23
24#[cfg(feature = "wasmbus")]
25use crate::wasmbus_model::Wasmbus;
26use crate::{
27    config::{LanguageConfig, OutputLanguage},
28    error::{print_warning, Error, Result},
29    format::{self, SourceFormatter},
30    gen::CodeGen,
31    model::{
32        get_operation, get_sorted_fields, get_trait, is_opt_namespace, value_to_json,
33        wasmcloud_actor_namespace, wasmcloud_core_namespace, wasmcloud_model_namespace,
34        CommentKind, PackageName, Ty,
35    },
36    render::Renderer,
37    writer::Writer,
38    BytesMut, ParamMap,
39};
40/// declarations for sorting. First sort key is the type (simple, then map, then struct).
41#[derive(Eq, Ord, PartialOrd, PartialEq)]
42struct Declaration(u8, BytesMut);
43
44type ShapeList<'model> = Vec<(&'model ShapeID, &'model AppliedTraits, &'model ShapeKind)>;
45
46fn codec_crate(has_cbor: bool) -> &'static str {
47    if has_cbor {
48        "cbor"
49    } else {
50        "msgpack"
51    }
52}
53
54// cbor function name modifier
55fn codec_pfx(has_cbor: bool) -> &'static str {
56    if has_cbor {
57        "C"
58    } else {
59        "M"
60    }
61}
62
63enum MethodSigType {
64    Interface,
65    Sender(String),
66    //Receiver,
67}
68
69#[derive(Clone, Copy)]
70pub(crate) enum DecodeRef {
71    Plain,
72    ByRef,
73}
74impl fmt::Display for DecodeRef {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(
77            f,
78            "{}",
79            match self {
80                DecodeRef::Plain => "d",
81                DecodeRef::ByRef => "&d",
82            }
83        )
84    }
85}
86
87#[derive(Default)]
88#[allow(dead_code)]
89pub struct GoCodeGen<'model> {
90    /// if set, limits declaration output to this namespace only
91    pub(crate) namespace: Option<NamespaceID>,
92    pub(crate) packages: HashMap<String, PackageName>,
93    pub(crate) import_core: String,
94    pub(crate) model: Option<&'model Model>,
95    pub(crate) package: String,
96    pub(crate) is_tinygo: bool,
97}
98
99impl<'model> GoCodeGen<'model> {
100    pub fn new(model: Option<&'model Model>, is_tinygo: bool) -> Self {
101        Self {
102            model,
103            namespace: None,
104            packages: HashMap::default(),
105            import_core: String::default(),
106            package: String::default(),
107            is_tinygo,
108        }
109    }
110}
111
112struct ServiceInfo<'model> {
113    id: &'model Identifier,
114    traits: &'model AppliedTraits,
115    service: &'model Service,
116}
117
118impl<'model> ServiceInfo<'model> {
119    fn wasmbus_contract_id(&self) -> Option<String> {
120        match get_trait(self.traits, crate::model::wasmbus_trait()) {
121            Ok(Some(Wasmbus { contract_id: Some(contract_id), .. })) => Some(contract_id),
122            _ => None,
123        }
124    }
125}
126
127#[non_exhaustive]
128enum MethodArgFlags {
129    Normal,
130    // ToString,
131}
132
133/// Returns the nil or zero of the type
134pub fn zero_of(id: &ShapeID, kind: Option<&ShapeKind>) -> &'static str {
135    if let Some(ShapeKind::Simple(simple)) = kind {
136        // matching on kind is preferable to using id, because it de-aliases to get to the root type
137        return match simple {
138            Simple::Blob => "make([]byte,0)",
139            Simple::Boolean => "false",
140            Simple::String => "\"\"",
141            Simple::Byte
142            | Simple::Short
143            | Simple::Integer
144            | Simple::Long
145            | Simple::Float
146            | Simple::Double => "0",
147            _ => "nil",
148        };
149    }
150
151    if id.namespace() == prelude_namespace_id() {
152        return match id.shape_name().to_string().as_str() {
153            SHAPE_BYTE | SHAPE_SHORT | SHAPE_INTEGER | SHAPE_LONG | SHAPE_FLOAT | SHAPE_DOUBLE => {
154                "0"
155            }
156            SHAPE_BOOLEAN => "false",
157            // technically this should be 'nil', but if someone accidentally serializes it,
158            // it will cause a panic in the msgpack decoder, so using empty array to avoid panics.
159            SHAPE_BLOB => "make([]byte,0)",
160            SHAPE_STRING => "\"\"",
161            // BigInteger, BigDecimal, Timestamp, Document, unit
162            _ => "nil",
163        };
164    }
165    if id.namespace() == wasmcloud_model_namespace() {
166        return match id.shape_name().to_string().as_str() {
167            "U64" | "U32" | "U16" | "U8" | "I64" | "I32" | "I16" | "I8" | "F64" | "F32" => "0",
168            _ => "nil",
169        };
170    }
171    "nil"
172}
173
174/// Returns true if the type is returned by value
175///  (i.e., not nillable)
176pub fn by_value(id: &ShapeID) -> bool {
177    let name = id.shape_name().to_string();
178    (id.namespace() == prelude_namespace_id()
179        && matches!(
180            name.as_str(),
181            SHAPE_BYTE
182                | SHAPE_SHORT
183                | SHAPE_INTEGER
184                | SHAPE_LONG
185                | SHAPE_FLOAT
186                | SHAPE_DOUBLE
187                | SHAPE_BOOLEAN
188                | SHAPE_BLOB
189                | SHAPE_STRING
190        ))
191        || (id.namespace() == wasmcloud_model_namespace()
192            && matches!(
193                name.as_str(),
194                "U64" | "U32" | "U16" | "U8" | "I64" | "I32" | "I16" | "I8" | "F64" | "F32"
195            ))
196}
197
198impl<'model> CodeGen for GoCodeGen<'model> {
199    fn output_language(&self) -> OutputLanguage {
200        if self.is_tinygo {
201            OutputLanguage::TinyGo
202        } else {
203            OutputLanguage::Go
204        }
205    }
206
207    /// Initialize code generator and renderer for language output.j
208    /// This hook is called before any code is generated and can be used to initialize code generator
209    /// and/or perform additional processing before output files are created.
210    fn init(
211        &mut self,
212        model: Option<&Model>,
213        _lc: &LanguageConfig,
214        _output_dir: Option<&Path>,
215        _renderer: &mut Renderer,
216    ) -> std::result::Result<(), Error> {
217        self.namespace = None;
218        if let Some(model) = model {
219            if let Some(Value::Array(codegen_min)) = model.metadata_value("codegen") {
220                let current_ver =
221                    semver::Version::parse(env!("CARGO_PKG_VERSION")).map_err(|e| {
222                        Error::InvalidModel(format!(
223                            "parse error for weld-codegen package version: {e}"
224                        ))
225                    })?;
226                for val in codegen_min.iter() {
227                    if let Value::Object(map) = val {
228                        if let Some(Value::String(lang)) = map.get("language") {
229                            if lang.as_str() == "go" {
230                                if let Some(Value::String(ver)) = map.get("min_version") {
231                                    let min_ver = semver::Version::parse(ver).map_err(|e| {
232                                        Error::InvalidModel(format!(
233                                            "metadata parse error for codegen {{ language=go, \
234                                             min_version={ver} }}: {e}"
235                                        ))
236                                    })?;
237                                    if min_ver.gt(&current_ver) {
238                                        return Err(Error::Model(format!(
239                                            "model requires weld-codegen version >= {min_ver}"
240                                        )));
241                                    }
242                                } else {
243                                    return Err(Error::Model(
244                                        "missing 'min_version' in metadata.codegen for lang=go"
245                                            .to_string(),
246                                    ));
247                                }
248                            }
249                        }
250                    }
251                }
252            }
253            if let Some(packages) = model.metadata_value("package") {
254                let packages: Vec<PackageName> = serde_json::from_value(value_to_json(packages))
255                    .map_err(|e| {
256                        Error::Model(format!(
257                            "invalid metadata format for package, expecting format \
258                             '[{{namespace:\"org.example\",crate:\"path::module\"}}]':  {e}"
259                        ))
260                    })?;
261                for p in packages.iter() {
262                    self.packages.insert(p.namespace.to_string(), p.clone());
263                }
264            }
265        }
266        Ok(())
267    }
268
269    /// Set up go formatter based on 'tinygo.formatter' settings in codegen.toml
270    fn source_formatter(&self, mut args: Vec<String>) -> Result<Box<dyn SourceFormatter>> {
271        if args.is_empty() {
272            return Err(Error::Formatter("missing tinygo.formatter setting".into()));
273        }
274        let program = args.remove(0);
275        Ok(Box::new(GoSourceFormatter { program, args }))
276    }
277
278    /// Perform any initialization required prior to code generation for a file
279    /// `model` may be used to check model metadata
280    /// `namespace` is the namespace in the model to generate
281    #[allow(unused_variables)]
282    fn init_file(
283        &mut self,
284        w: &mut Writer,
285        model: &Model,
286        file_config: &crate::config::OutputFile,
287        params: &ParamMap,
288    ) -> Result<()> {
289        self.namespace = match &file_config.namespace {
290            Some(ns) => Some(NamespaceID::from_str(ns)?),
291            None => {
292                return Err(Error::Other(format!(
293                    "namespace must be defined (in codegen.toml) for go output file {}",
294                    file_config.path.display()
295                )));
296            }
297        };
298        let ns = self.namespace.as_ref().unwrap();
299        if self.packages.get(&ns.to_string()).is_none() {
300            print_warning(&format!(
301                concat!(
302                    "no package metadata defined for namespace {}.",
303                    " Add a declaration like this at the top of fhe .smithy file: ",
304                    " metadata package = [ {{ namespace: \"{}\", crate: \"crate_name\" }} ]"
305                ),
306                ns, ns
307            ));
308        }
309        self.package = if let Some(toml::Value::String(package)) = file_config.params.get("package")
310        {
311            package.to_string()
312        } else {
313            match ns.to_string().rsplit_once('.') {
314                Some((_, last)) => last.to_string(),
315                None => self.namespace.as_ref().unwrap().to_string(),
316            }
317        };
318        self.import_core = if ns == wasmcloud_model_namespace() || ns == wasmcloud_core_namespace()
319        {
320            String::new()
321        } else {
322            "actor.".to_string()
323        };
324        Ok(())
325    }
326
327    fn write_source_file_header(
328        &mut self,
329        w: &mut Writer,
330        _model: &Model,
331        _params: &ParamMap,
332    ) -> Result<()> {
333        if let Some(package_doc) = self
334            .packages
335            .get(&self.namespace.as_ref().unwrap().to_string())
336            .and_then(|p| p.doc.as_ref())
337        {
338            // for 'godoc', package-level documentation before the package declaration
339            writeln!(w, "// {}", &package_doc).unwrap();
340        }
341        let ns = self.namespace.as_ref().unwrap();
342        writeln!(
343            w,
344            r#"package {}
345            import (
346                msgpack "github.com/wasmcloud/tinygo-msgpack" //nolint
347                cbor "github.com/wasmcloud/tinygo-cbor" //nolint
348                {}
349            )"#,
350            &self.package,
351            if ns == wasmcloud_actor_namespace() {
352                "core \"github.com/wasmcloud/interfaces/core/tinygo\" //nolint"
353            } else if ns != wasmcloud_model_namespace() && ns != wasmcloud_core_namespace() {
354                "actor \"github.com/wasmcloud/actor-tinygo\" //nolint"
355            } else {
356                // avoid circular dependencies - core and model are part of the actor-tinygo package
357                ""
358            }
359        )
360        .unwrap();
361        Ok(())
362    }
363
364    /// Complete generation and return the output bytes
365    fn finalize(&mut self, w: &mut Writer) -> Result<bytes::Bytes> {
366        writeln!(
367            w,
368            "\n// This file is generated automatically using wasmcloud/weld-codegen {}",
369            env!("CARGO_PKG_VERSION"),
370        )
371        .unwrap();
372        Ok(w.take().freeze())
373    }
374
375    fn declare_types(&mut self, w: &mut Writer, model: &Model, params: &ParamMap) -> Result<()> {
376        let ns = self.namespace.clone();
377
378        let mut shapes = model
379            .shapes()
380            .filter(|s| is_opt_namespace(s.id(), &ns))
381            .map(|s| (s.id(), s.traits(), s.body()))
382            .collect::<ShapeList>();
383        // sort shapes (they are all in the same namespace if ns.is_some(), which is usually true)
384        shapes.sort_by_key(|v| v.0);
385
386        for (id, traits, shape) in shapes.into_iter() {
387            // traits are only needed for the rust implementation of codegen, so skip them in go
388            if traits.contains_key(&prelude_shape_named(TRAIT_TRAIT).unwrap()) {
389                continue;
390            }
391            let mut want_serde = !params.contains_key("no_serde");
392            match shape {
393                ShapeKind::Simple(simple) => {
394                    self.declare_simple_shape(w, id.shape_name(), traits, simple)?;
395                    // don't generate encoder/decoder for primitive type aliases
396                    want_serde = want_serde
397                        && (id.namespace() != wasmcloud_model_namespace()
398                            || !matches!(
399                                id.shape_name().to_string().as_str(),
400                                "F64"
401                                    | "F32"
402                                    | "U64"
403                                    | "U32"
404                                    | "U16"
405                                    | "U8"
406                                    | "I64"
407                                    | "I32"
408                                    | "I16"
409                                    | "I8"
410                            ));
411                }
412                ShapeKind::Map(map) => {
413                    self.declare_map_shape(w, id.shape_name(), traits, map)?;
414                }
415                ShapeKind::List(list) => {
416                    self.declare_list_or_set_shape(w, id.shape_name(), traits, list)?;
417                }
418                ShapeKind::Set(set) => {
419                    self.declare_list_or_set_shape(w, id.shape_name(), traits, set)?;
420                }
421                ShapeKind::Structure(strukt) => {
422                    self.declare_structure_shape(w, id, traits, strukt)?;
423                }
424                ShapeKind::Union(_strukt) => {
425                    eprintln!(
426                        "Warning: Union types are not currently supported for Go: skipping {}",
427                        id.shape_name()
428                    );
429                    want_serde = false;
430                    //TODO: union
431                    // self.declare_union_shape(w, id, traits, strukt)?;
432                }
433                ShapeKind::Operation(_)
434                | ShapeKind::Resource(_)
435                | ShapeKind::Service(_)
436                | ShapeKind::Unresolved => {
437                    want_serde = false;
438                }
439            }
440            if want_serde {
441                self.declare_codec(w, id, shape, false)?;
442                self.declare_codec(w, id, shape, true)?;
443            }
444        }
445        Ok(())
446    }
447
448    fn write_services(&mut self, w: &mut Writer, model: &Model, _params: &ParamMap) -> Result<()> {
449        let ns = self.namespace.clone();
450        let mut services: Vec<(&ShapeID, &AppliedTraits, &ShapeKind)> = model
451            .shapes()
452            .filter(|s| is_opt_namespace(s.id(), &ns))
453            .map(|s| (s.id(), s.traits(), s.body()))
454            .collect();
455        // sort services in this namespace, so output order is deterministic
456        services.sort_by_key(|me| me.0);
457        for (id, traits, shape) in services.iter() {
458            if let ShapeKind::Service(service) = shape {
459                let service = ServiceInfo { id: id.shape_name(), service, traits };
460                self.write_service_interface(w, model, &service)?;
461                self.write_service_receiver(w, model, &service)?;
462                self.write_service_sender(w, model, &service)?;
463            }
464        }
465        Ok(())
466    }
467
468    /// Write a single-line comment
469    fn write_comment(&mut self, w: &mut Writer, _kind: CommentKind, line: &str) {
470        // all comment types same for Go
471        writeln!(w, "// {line}").unwrap();
472    }
473
474    /// generate Go method name: capitalized to make public
475    fn to_method_name_case(&self, name: &str) -> String {
476        crate::strings::to_pascal_case(name)
477    }
478
479    /// generate Go field name: capitalized to make public
480    fn to_field_name_case(&self, name: &str) -> String {
481        crate::strings::to_pascal_case(name)
482    }
483
484    /// generate Go type name: PascalCase
485    fn to_type_name_case(&self, name: &str) -> String {
486        crate::strings::to_pascal_case(name)
487    }
488
489    /// returns Go source file extension
490    fn get_file_extension(&self) -> &'static str {
491        "go"
492    }
493}
494
495// generate code to invoke encoder, handling alias if applicable. Generates, for example
496//      encode_alias!(id,"val","String","String","string")
497// generates
498//      "encoder.WriteString(val)"  or "encoder.WriteString(string(*val))"
499// depending on whether 'id' is a prelude simple shape (left side) or an alias (right side)
500macro_rules! encode_alias {
501    ( $id:ident, $var:ident, $shape:expr, $encodeFn:expr, $cast:expr ) => {
502        format!(
503            "encoder.Write{}({})",
504            $encodeFn,
505            if $id == &ShapeID::new_unchecked(PRELUDE_NAMESPACE, $shape, None) {
506                $var.to_string()
507            } else {
508                format!("{}(*{})", $cast, $var)
509            }
510        )
511    };
512}
513
514impl<'model> GoCodeGen<'model> {
515    /// Write encoder and decoder for top-level shapes in this package
516    fn declare_codec(
517        &self,
518        w: &mut Writer,
519        id: &ShapeID,
520        kind: &ShapeKind,
521        has_cbor: bool,
522    ) -> Result<()> {
523        let name = self.type_string(Ty::Shape(id))?;
524        let (decode_fn, base_shape) = self.shape_decoder(id, kind, has_cbor)?;
525        let fn_prefix = codec_pfx(has_cbor);
526        let serde_crate = codec_crate(has_cbor);
527        writeln!(
528            w,
529            r#"// {}Encode serializes a {} using {}
530            func (o *{}) {}Encode(encoder {}.Writer) error {{
531                {}
532                return encoder.CheckError()
533            }}
534            
535            // {}Decode{} deserializes a {} using {}
536            func {}Decode{}(d *{}.Decoder) ({},error) {{
537                {}
538            }}"#,
539            fn_prefix,
540            &name,
541            serde_crate,
542            &name,
543            fn_prefix,
544            serde_crate,
545            self.shape_encoder(id, kind, "o", has_cbor)?,
546            //
547            fn_prefix,
548            &name,
549            &name,
550            serde_crate,
551            fn_prefix,
552            &name,
553            serde_crate,
554            &name,
555            if self.is_decoder_function(kind) {
556                decode_fn
557            } else {
558                format!(
559                    r#"val,err := {}
560                  if err != nil {{
561                    return {},err
562                  }}
563                  return {},nil"#,
564                    decode_fn,
565                    zero_of(&base_shape, Some(kind)), // not needed anymore
566                    if &base_shape != id {
567                        format!(
568                            "{}(val)",
569                            self.to_type_name_case(&id.shape_name().to_string())
570                        )
571                    } else {
572                        "val".into()
573                    }
574                )
575            }
576            .trim_end_matches('\n')
577        )
578        .unwrap();
579        Ok(())
580    }
581
582    /// Apply documentation traits: (documentation, deprecated, unstable)
583    fn apply_documentation_traits(
584        &mut self,
585        w: &mut Writer,
586        id: &Identifier,
587        traits: &AppliedTraits,
588    ) {
589        if let Some(Some(Value::String(text))) =
590            traits.get(&prelude_shape_named(TRAIT_DOCUMENTATION).unwrap())
591        {
592            self.write_documentation(w, id, text);
593        }
594
595        // '@deprecated' trait
596        if let Some(Some(Value::Object(map))) =
597            traits.get(&prelude_shape_named(TRAIT_DEPRECATED).unwrap())
598        {
599            w.write(b"// @deprecated ");
600            if let Some(Value::String(since)) = map.get("since") {
601                w.write(&format!("since=\"{since}\"\n"));
602            }
603            if let Some(Value::String(message)) = map.get("message") {
604                w.write(&format!("note=\"{message}\"\n"));
605            }
606            w.write(b")\n");
607        }
608
609        // '@unstable' trait
610        if traits.get(&prelude_shape_named(TRAIT_UNSTABLE).unwrap()).is_some() {
611            self.write_comment(w, CommentKind::Documentation, "@unstable");
612        }
613    }
614
615    /// Write a type name, a primitive or defined type, with or without deref('&') and with or without Option<>
616    /// The lifetime parameter will only be used if the type requires a lifetime
617    pub(crate) fn type_string(&self, ty: Ty<'_>) -> Result<String> {
618        let mut s = String::new();
619        match ty {
620            Ty::Opt(id) => {
621                s.push_str(&self.type_string(Ty::Shape(id))?);
622            }
623            Ty::Ref(id) => {
624                s.push_str(&self.type_string(Ty::Shape(id))?);
625            }
626            Ty::Ptr(id) => {
627                if !by_value(id) {
628                    s.push('*');
629                }
630                s.push_str(&self.type_string(Ty::Shape(id))?);
631            }
632            Ty::Shape(id) => {
633                let name = id.shape_name().to_string();
634                if id.namespace() == prelude_namespace_id() {
635                    let ty: String = match name.as_ref() {
636                        SHAPE_BLOB => "[]byte".into(),
637                        SHAPE_BOOLEAN | SHAPE_PRIMITIVEBOOLEAN => "bool".into(),
638                        SHAPE_STRING => "string".into(),
639                        SHAPE_BYTE | SHAPE_PRIMITIVEBYTE => "int8".into(),
640                        SHAPE_SHORT | SHAPE_PRIMITIVESHORT => "int16".into(),
641                        SHAPE_INTEGER | SHAPE_PRIMITIVEINTEGER => "int32".into(),
642                        SHAPE_LONG | SHAPE_PRIMITIVELONG => "int64".into(),
643                        SHAPE_FLOAT | SHAPE_PRIMITIVEFLOAT => "float32".into(),
644                        SHAPE_DOUBLE | SHAPE_PRIMITIVEDOUBLE => "float64".into(),
645                        SHAPE_DOCUMENT => format!("{}Document", self.import_core),
646                        SHAPE_TIMESTAMP => format!("{}Timestamp", self.import_core),
647                        SHAPE_BIGINTEGER => {
648                            // TODO: not supported
649                            todo!()
650                        }
651                        SHAPE_BIGDECIMAL => {
652                            // TODO: not supported
653                            todo!()
654                        }
655                        _ => return Err(Error::UnsupportedType(name)),
656                    };
657                    s.push_str(&ty);
658                } else if id.namespace() == wasmcloud_model_namespace() {
659                    match name.as_str() {
660                        "U64" | "U32" | "U16" | "U8" => {
661                            s.push_str("uint");
662                            s.push_str(&name[1..])
663                        }
664                        "I64" | "I32" | "I16" | "I8" => {
665                            s.push_str("int");
666                            s.push_str(&name[1..]);
667                        }
668                        "F64" => s.push_str("float64"),
669                        "F32" => s.push_str("float32"),
670                        _ => {
671                            if self.namespace.is_none()
672                                || self.namespace.as_ref().unwrap() != id.namespace()
673                            {
674                                s.push_str(&self.import_core);
675                            }
676                            s.push_str(&self.to_type_name_case(&name));
677                        }
678                    };
679                } else if self.namespace.is_some()
680                    && id.namespace() == self.namespace.as_ref().unwrap()
681                {
682                    // we are in the same namespace so we don't need to specify namespace
683                    s.push_str(&self.to_type_name_case(&id.shape_name().to_string()));
684                } else {
685                    s.push_str(&self.get_crate_path(id)?);
686                    s.push_str(&self.to_type_name_case(&id.shape_name().to_string()));
687                }
688            }
689        }
690        Ok(s)
691    }
692
693    // declaration for simple type
694    fn declare_simple_shape(
695        &mut self,
696        w: &mut Writer,
697        id: &Identifier,
698        traits: &AppliedTraits,
699        simple: &Simple,
700    ) -> Result<()> {
701        self.apply_documentation_traits(w, id, traits);
702        writeln!(
703            w,
704            "type {} {}",
705            &self.to_type_name_case(&id.to_string()),
706            match simple {
707                Simple::Blob => "[]byte".into(),
708                Simple::Boolean => "bool".into(),
709                Simple::String => "string".into(),
710                Simple::Byte => "int8".into(),
711                Simple::Short => "int16".into(),
712                Simple::Integer => "int32".into(),
713                Simple::Long => "int64".into(),
714                Simple::Float => "float32".into(),
715                Simple::Double => "float64".into(),
716                Simple::Document => format!("{}Document", &self.import_core),
717                Simple::Timestamp => format!("{}Timestamp", &self.import_core),
718                Simple::BigInteger => {
719                    // TODO: unsupported
720                    todo!()
721                }
722                Simple::BigDecimal => {
723                    // TODO: unsupported
724                    todo!()
725                }
726            }
727        )
728        .unwrap();
729        Ok(())
730    }
731
732    fn declare_map_shape(
733        &mut self,
734        w: &mut Writer,
735        id: &Identifier,
736        traits: &AppliedTraits,
737        shape: &MapShape,
738    ) -> Result<()> {
739        self.apply_documentation_traits(w, id, traits);
740        writeln!(
741            w,
742            "type {} map[{}]{}",
743            &self.to_type_name_case(&id.to_string()),
744            &self.type_string(Ty::Shape(shape.key().target()))?,
745            &self.type_string(Ty::Shape(shape.value().target()))?,
746        )
747        .unwrap();
748        Ok(())
749    }
750
751    fn declare_list_or_set_shape(
752        &mut self,
753        w: &mut Writer,
754        id: &Identifier,
755        traits: &AppliedTraits,
756        shape: &ListOrSet,
757    ) -> Result<()> {
758        self.apply_documentation_traits(w, id, traits);
759        writeln!(
760            w,
761            "type {} []{}",
762            &self.to_type_name_case(&id.to_string()),
763            &self.type_string(Ty::Shape(shape.member().target()))?
764        )
765        .unwrap();
766        Ok(())
767    }
768
769    fn declare_structure_shape(
770        &mut self,
771        w: &mut Writer,
772        id: &ShapeID,
773        traits: &AppliedTraits,
774        strukt: &StructureOrUnion,
775    ) -> Result<()> {
776        let ident = id.shape_name();
777        self.apply_documentation_traits(w, ident, traits);
778        writeln!(
779            w,
780            "type {} struct {{",
781            &self.to_type_name_case(&ident.to_string())
782        )
783        .unwrap();
784        let (fields, _is_numbered) = get_sorted_fields(ident, strukt)?;
785        for member in fields.iter() {
786            self.apply_documentation_traits(w, member.id(), member.traits());
787            let (field_name, ser_name) = self.get_field_name_and_ser_name(member)?;
788            let target = member.target();
789            let field_tags = format!(r#"`json:"{ser_name}"`"#);
790            writeln!(
791                w,
792                "  {} {} {}",
793                &field_name,
794                self.type_string(if is_optional_field(member, self.shape_kind(target)) {
795                    Ty::Ptr(target)
796                } else {
797                    Ty::Shape(target)
798                })?,
799                field_tags,
800            )
801            .unwrap();
802        }
803        w.write(b"}\n\n");
804        Ok(())
805    }
806
807    #[allow(dead_code)]
808    fn declare_union_shape(
809        &mut self,
810        w: &mut Writer,
811        id: &ShapeID,
812        traits: &AppliedTraits,
813        strukt: &StructureOrUnion,
814    ) -> Result<()> {
815        let ident = id.shape_name();
816        let (fields, is_numbered) = get_sorted_fields(ident, strukt)?;
817        if !is_numbered {
818            return Err(Error::Model(format!(
819                "union {ident} must have numbered fields"
820            )));
821        }
822        self.apply_documentation_traits(w, ident, traits);
823        writeln!(w, "// enum {ident}").unwrap();
824        writeln!(w, "type {ident} uint16").unwrap();
825        writeln!(w, "const (").unwrap();
826        let mut first_value = true;
827        for member in fields.iter() {
828            self.apply_documentation_traits(w, member.id(), member.traits());
829            let field_num = member.field_num().unwrap();
830            let variant_name = self.to_type_name_case(&member.id().to_string());
831            if member.target() == crate::model::unit_shape() {
832                if first_value {
833                    writeln!(w, "    {variant_name} {ident} = {field_num}").unwrap();
834                    first_value = false;
835                } else {
836                    writeln!(w, "    {variant_name} = {field_num}").unwrap();
837                }
838            } else {
839                // TODO: not supported yet
840                w.write(&format!(
841                    "{}({}),\n",
842                    variant_name,
843                    self.type_string(Ty::Shape(member.target()))?
844                ));
845            }
846        }
847        w.write(b")\n\n");
848
849        // generate String method
850        writeln!(w, "func (x {ident}) String() string {{").unwrap();
851        w.write(b"  switch x {\n");
852        for member in fields.iter() {
853            let variant_name = self.to_type_name_case(&member.id().to_string());
854            writeln!(
855                w,
856                "    case {variant_name}:\n      return \"{variant_name}\""
857            )
858            .unwrap();
859        }
860        w.write(b"  }\n  return \"UNDEFINED\"\n}\n");
861
862        Ok(())
863    }
864
865    /// Declares the service as a go interface whose methods are the smithy service operations
866    fn write_service_interface(
867        &mut self,
868        w: &mut Writer,
869        model: &Model,
870        service: &ServiceInfo,
871    ) -> Result<()> {
872        self.apply_documentation_traits(w, service.id, service.traits);
873        writeln!(w, "type {} interface {{", &service.id).unwrap();
874        for operation in service.service.operations() {
875            // if operation is not declared in this namespace, don't define it here
876            if let Some(ref ns) = self.namespace {
877                if operation.namespace() != ns {
878                    continue;
879                }
880            }
881            let (op, op_traits) = get_operation(model, operation, service.id)?;
882            let method_id = operation.shape_name();
883            let _flags =
884                self.write_method_signature(w, method_id, op_traits, op, MethodSigType::Interface)?;
885            w.write(b"\n");
886        }
887        w.write(b"}\n\n");
888
889        writeln!(
890            w,
891            r#"
892        // {}Handler is called by an actor during `main` to generate a dispatch handler
893        // The output of this call should be passed into `actor.RegisterHandlers`
894        func {}Handler(actor_ {}) {}Handler {{
895            return {}NewHandler("{}", &{}Receiver{{}}, actor_)
896        }}"#,
897            &service.id,
898            &service.id,
899            &service.id,
900            &self.import_core,
901            &self.import_core,
902            &service.id,
903            &service.id,
904        )
905        .unwrap();
906
907        self.write_service_contract_getter(w, service)?;
908        Ok(())
909    }
910
911    /// add getter for capability contract id
912    fn write_service_contract_getter(
913        &mut self,
914        w: &mut Writer,
915        service: &ServiceInfo,
916    ) -> Result<()> {
917        if let Some(contract_id) = service.wasmbus_contract_id() {
918            writeln!(
919                w,
920                r#"// {}ContractId returns the capability contract id for this interface
921                func {}ContractId() string {{ return "{}" }} 
922                "#,
923                service.id, service.id, contract_id
924            )
925            .unwrap();
926        }
927        Ok(())
928    }
929
930    /// write trait function declaration "async fn method(args) -> Result< return_type, actor.RpcError >"
931    /// does not write trailing semicolon so this can be used for declaration and implementation
932    fn write_method_signature(
933        &mut self,
934        w: &mut Writer,
935        method_id: &Identifier,
936        method_traits: &AppliedTraits,
937        op: &Operation,
938        sig_type: MethodSigType,
939    ) -> Result<MethodArgFlags> {
940        let method_name = self.to_method_name(method_id, method_traits);
941        let arg_flags = MethodArgFlags::Normal;
942        self.apply_documentation_traits(w, method_id, method_traits);
943        match sig_type {
944            MethodSigType::Interface => {}
945            MethodSigType::Sender(s) => write!(w, "func (s *{s}Sender) ").unwrap(),
946        }
947        w.write(&method_name);
948        write!(w, "(ctx *{}Context", &self.import_core).unwrap();
949        // optional input parameter
950        if let Some(input_type) = op.input() {
951            write!(w, ", arg {}", self.type_string(Ty::Ref(input_type))?).unwrap();
952        }
953        w.write(b") ");
954        // return type with output output parameter
955        if let Some(output_type) = op.output() {
956            write!(w, "({}, error)", self.type_string(Ty::Ptr(output_type))?).unwrap();
957        } else {
958            w.write(b"error");
959        }
960        Ok(arg_flags)
961    }
962
963    // pub trait FooReceiver : MessageDispatch + Foo { ... }
964    fn write_service_receiver(
965        &mut self,
966        w: &mut Writer,
967        model: &Model,
968        service: &ServiceInfo,
969    ) -> Result<()> {
970        let doc = format!(
971            "{}Receiver receives messages defined in the {} service interface",
972            service.id, service.id
973        );
974        self.write_comment(w, CommentKind::Documentation, &doc);
975        self.apply_documentation_traits(w, service.id, service.traits);
976        let proto = crate::model::wasmbus_proto(service.traits)?;
977        let has_cbor = proto.map(|pv| pv.has_cbor()).unwrap_or(false);
978        writeln!(w, "type {}Receiver struct {{}}", service.id).unwrap();
979        writeln!(
980            w,
981            r#"func (r* {}Receiver) Dispatch(ctx *{}Context, svc interface{{}}, message *{}Message) (*{}Message, error) {{
982                svc_,_ := svc.({})
983                switch message.Method {{
984                 "#,
985            &service.id,
986            &self.import_core,
987            &self.import_core,
988            &self.import_core,
989            &service.id,
990        ).unwrap();
991
992        for method_id in service.service.operations() {
993            // we don't add operations defined in another namespace
994            if let Some(ref ns) = self.namespace {
995                if method_id.namespace() != ns {
996                    continue;
997                }
998            }
999            let method_ident = method_id.shape_name();
1000            let (op, method_traits) = get_operation(model, method_id, service.id)?;
1001            w.write(b"case \"");
1002            w.write(&self.op_dispatch_name(method_ident));
1003            w.write(b"\" : {\n");
1004            if let Some(op_input) = op.input() {
1005                // decode input into 'value'
1006                writeln!(
1007                    w,
1008                    r#"
1009                        d := {}.NewDecoder(message.Arg)
1010                        value,err_ := {}
1011                        if err_ != nil {{ 
1012                            return nil,err_
1013                        }}
1014                        "#,
1015                    codec_crate(has_cbor),
1016                    self.value_decoder(op_input, DecodeRef::ByRef, has_cbor)?,
1017                )
1018                .unwrap();
1019            }
1020            // resp, err := svc_.method(ctx, &value);
1021            let method_name = self.to_method_name(method_ident, method_traits);
1022            writeln!(
1023                w,
1024                r#"{} := svc_.{} (ctx{})
1025                if err != nil {{ 
1026                    return nil,err
1027                }}"#,
1028                if op.output().is_some() { "resp, err" } else { "err" },
1029                &method_name,
1030                if op.has_input() { ", value" } else { "" },
1031            )
1032            .unwrap();
1033            // serialize result
1034            if let Some(op_output) = op.output() {
1035                writeln!(
1036                    w,
1037                    r#"
1038            	    var sizer {}.Sizer
1039            	    size_enc := &sizer
1040            	    {} 
1041            	    buf := make([]byte, sizer.Len())
1042            	    encoder := {}.NewEncoder(buf)
1043            	    enc := &encoder
1044                    {}"#,
1045                    codec_crate(has_cbor),
1046                    self.value_encoder(op_output, "resp", "size_enc", has_cbor)?,
1047                    codec_crate(has_cbor),
1048                    self.value_encoder(op_output, "resp", "enc", has_cbor)?,
1049                )
1050                .unwrap();
1051            } else {
1052                w.write(b"buf := make([]byte, 0)\n");
1053            }
1054            // return result,
1055            writeln!(
1056                w,
1057                r#" return &{}Message {{ Method: "{}", Arg: buf }}, nil
1058                    }}"#,
1059                &self.import_core,
1060                &self.full_dispatch_name(service.id, method_ident),
1061            )
1062            .unwrap();
1063        }
1064        // handle fallthrough case of unrcognied operation
1065        // end case, end dispatch function
1066        writeln!(
1067            w,
1068            r#"default: 
1069                   return nil, {}NewRpcError("MethodNotHandled", "{}." + message.Method)
1070               }}
1071            }}
1072            "#,
1073            &self.import_core, service.id,
1074        )
1075        .unwrap();
1076        Ok(())
1077    }
1078
1079    /// writes the service sender struct and constructor
1080    fn write_service_sender(
1081        &mut self,
1082        w: &mut Writer,
1083        model: &Model,
1084        service: &ServiceInfo,
1085    ) -> Result<()> {
1086        let doc = format!(
1087            "{}Sender sends messages to a {} service",
1088            service.id, service.id
1089        );
1090        self.write_comment(w, CommentKind::Documentation, &doc);
1091        self.apply_documentation_traits(w, service.id, service.traits);
1092        let proto = crate::model::wasmbus_proto(service.traits)?;
1093        let has_cbor = proto.map(|pv| pv.has_cbor()).unwrap_or(false);
1094        let core_prefix = &self.import_core;
1095        writeln!(
1096            w,
1097            r#"type {}Sender struct {{ transport {}Transport }}
1098            {}
1099            {}"#,
1100            service.id,
1101            core_prefix,
1102            self.actor_receive_sender_constructors(service.id, service.traits)?,
1103            self.provider_receive_sender_constructors(service.id, service.traits)?,
1104        )
1105        .unwrap();
1106
1107        for method_id in service.service.operations() {
1108            // we don't add operations defined in another namespace
1109            if let Some(ref ns) = self.namespace {
1110                if method_id.namespace() != ns {
1111                    continue;
1112                }
1113            }
1114            let method_ident = method_id.shape_name();
1115            let (op, op_traits) = get_operation(model, method_id, service.id)?;
1116            let _arg_is_string = false;
1117            let _flags = self.write_method_signature(
1118                w,
1119                method_ident,
1120                op_traits,
1121                op,
1122                MethodSigType::Sender(service.id.to_string()),
1123            )?;
1124            w.write(b" {\n");
1125            if let Some(op_input) = op.input() {
1126                writeln!(
1127                    w,
1128                    r#"
1129            	    var sizer {}.Sizer
1130            	    size_enc := &sizer
1131            	    {} 
1132            	    buf := make([]byte, sizer.Len())
1133            	    
1134            	    var encoder = {}.NewEncoder(buf)
1135            	    enc := &encoder
1136                    {}
1137            	"#,
1138                    codec_crate(has_cbor),
1139                    self.value_encoder(op_input, "arg", "size_enc", has_cbor,)?,
1140                    codec_crate(has_cbor),
1141                    self.value_encoder(op_input, "arg", "enc", has_cbor,)?,
1142                )
1143                .unwrap();
1144            } else {
1145                w.write(b"buf := make([]byte,0)\n");
1146            }
1147            writeln!(
1148                w,
1149                r#"{}s.transport.Send(ctx, {}Message{{ Method: "{}", Arg:buf }})"#,
1150                if op.output().is_some() { "out_buf,_ := " } else { "" },
1151                &self.import_core,
1152                &self.full_dispatch_name(service.id, method_ident),
1153            )
1154            .unwrap();
1155            if let Some(op_output) = op.output() {
1156                let out_kind = self.shape_kind(op_output);
1157                writeln!(
1158                    w,
1159                    r#"d := {}.NewDecoder(out_buf)
1160                        resp,err_ := {}
1161                        if err_ != nil {{ 
1162                            return {},err_
1163                        }}
1164                        return {}resp,nil
1165                     }}"#,
1166                    codec_crate(has_cbor),
1167                    self.value_decoder(op_output, DecodeRef::ByRef, has_cbor)?,
1168                    zero_of(op_output, out_kind),
1169                    // use ptr for nillable return types
1170                    if by_value(op_output) { "" } else { "&" }
1171                )
1172                .unwrap();
1173            } else {
1174                w.write(b"return nil\n}\n");
1175            }
1176        }
1177        Ok(())
1178    }
1179
1180    /// add sender constructors for calling actors, for services that declare actorReceive
1181    #[cfg(feature = "wasmbus")]
1182    fn actor_receive_sender_constructors(
1183        &self,
1184        service_id: &Identifier,
1185        service_traits: &AppliedTraits,
1186    ) -> Result<String> {
1187        let ctors = if let Some(Wasmbus { actor_receive: true, .. }) =
1188            get_trait(service_traits, crate::model::wasmbus_trait())?
1189        {
1190            format!(
1191                r#"
1192                // NewActorSender constructs a client for actor-to-actor messaging
1193                // using the recipient actor's public key
1194                func NewActor{}Sender(actor_id string) *{}Sender {{
1195                    transport := {}ToActor(actor_id)
1196                    return &{}Sender {{ transport: transport }}
1197                }}
1198                "#,
1199                service_id, service_id, &self.import_core, service_id
1200            )
1201        } else {
1202            String::new()
1203        };
1204        Ok(ctors)
1205    }
1206
1207    /// add sender constructors for actors calling providers
1208    /// This is only used for wasm32 targets and for services that declare 'providerReceive'
1209    #[cfg(feature = "wasmbus")]
1210    fn provider_receive_sender_constructors(
1211        &self,
1212        service_id: &Identifier,
1213        service_traits: &AppliedTraits,
1214    ) -> Result<String> {
1215        let ctors = if let Some(Wasmbus {
1216            provider_receive: true,
1217            contract_id: Some(contract),
1218            ..
1219        }) = get_trait(service_traits, crate::model::wasmbus_trait())?
1220        {
1221            format!(
1222                r#"
1223                // NewProvider constructs a client for sending to a {} provider
1224                // implementing the '{}' capability contract, with the "default" link
1225                func NewProvider{}() *{}Sender {{
1226                    transport := {}ToProvider("{}", "default")
1227                    return &{}Sender {{ transport: transport }}
1228                }}
1229
1230                // NewProvider{}Link constructs a client for sending to a {} provider
1231                // implementing the '{}' capability contract, with the specified link name
1232                func NewProvider{}Link(linkName string) *{}Sender {{
1233                    transport :=  {}ToProvider("{}", linkName)
1234                    return &{}Sender {{ transport: transport }}
1235                }}
1236                "#,
1237                // new provider
1238                service_id,
1239                contract,
1240                service_id,
1241                service_id,
1242                &self.import_core,
1243                contract,
1244                service_id,
1245                // new provider link
1246                service_id,
1247                service_id,
1248                contract,
1249                service_id,
1250                service_id,
1251                &self.import_core,
1252                contract,
1253                service_id,
1254            )
1255        } else {
1256            String::new()
1257        };
1258        Ok(ctors)
1259    }
1260
1261    /// returns the package prefix for the symbol, using metadata crate declarations
1262    pub(crate) fn get_crate_path(&self, id: &ShapeID) -> Result<String> {
1263        let namespace = id.namespace();
1264        if namespace == self.namespace.as_ref().unwrap() {
1265            return Ok(String::new());
1266        }
1267        if namespace == wasmcloud_model_namespace() || namespace == wasmcloud_core_namespace() {
1268            return Ok(self.import_core.clone());
1269        }
1270        if namespace == prelude_namespace_id() {
1271            if id.shape_name() == &Identifier::new_unchecked(SHAPE_TIMESTAMP)
1272                || id.shape_name() == &Identifier::new_unchecked(SHAPE_DOCUMENT)
1273            {
1274                return Ok(self.import_core.clone());
1275            }
1276            return Ok(String::new());
1277        }
1278
1279        // look up the crate name, which should be valid go syntax
1280        match self.packages.get(&namespace.to_string()) {
1281            Some(crate::model::PackageName { go_package: Some(go_package), .. }) => {
1282                Ok(format!("{go_package}."))
1283            }
1284            _ => Err(Error::Model(format!(
1285                "undefined go_package for namespace '{}' symbol '{}'. Make sure codegen.toml includes \
1286                 all dependent namespaces, and that the dependent .smithy file contains package \
1287                 metadata with go_package: value",
1288                namespace,
1289                id.shape_name(),
1290            ))),
1291        }
1292    }
1293
1294    /// Generate string to encode structure.
1295    /// Second Result field is true if structure has no fields, e.g., "MyStruct {}"
1296    fn struct_encode(
1297        &self,
1298        id: &ShapeID,
1299        strukt: &StructureOrUnion,
1300        val: &str,
1301        has_cbor: bool,
1302    ) -> Result<String> {
1303        let (fields, _) = crate::model::get_sorted_fields(id.shape_name(), strukt)?;
1304        let mut s = String::new();
1305        writeln!(s, "encoder.WriteMapSize({})", fields.len()).unwrap();
1306        for field in fields.iter() {
1307            let (field_name, ser_name) = self.get_field_name_and_ser_name(field)?;
1308            writeln!(s, "encoder.WriteString(\"{}\")", &ser_name).unwrap();
1309            let field_val = self.value_encoder(
1310                field.target(),
1311                &format!("{}.{}", val, &field_name),
1312                "encoder",
1313                has_cbor,
1314            )?;
1315            if is_optional_field(field, self.shape_kind(field.target())) {
1316                writeln!(
1317                    s,
1318                    r#"if {}.{} == nil {{
1319                        encoder.WriteNil()
1320                    }} else {{
1321                        {}
1322                    }}"#,
1323                    val, &field_name, &field_val
1324                )
1325                .unwrap();
1326            } else {
1327                writeln!(s, "{}", &field_val).unwrap();
1328            }
1329        }
1330        Ok(s)
1331    }
1332
1333    /// Generate string to decode structure.
1334    fn struct_decode(
1335        &self,
1336        id: &ShapeID,
1337        strukt: &StructureOrUnion,
1338        has_cbor: bool,
1339    ) -> Result<String> {
1340        let (fields, _) = crate::model::get_sorted_fields(id.shape_name(), strukt)?;
1341        let mut s = String::new();
1342        writeln!(
1343            s,
1344            r#"var val {}
1345            isNil,err := d.IsNextNil()
1346            if err != nil || isNil {{ 
1347                return val,err 
1348            }}
1349            {}
1350            if err != nil {{ return val,err }}
1351            for i := uint32(0); i < size; i++ {{
1352                field,err := d.ReadString()
1353                if err != nil {{ return val,err }}
1354                switch field {{"#,
1355            self.to_type_name_case(&id.shape_name().to_string()),
1356            if has_cbor {
1357                r#"size,indef,err := d.ReadMapSize()
1358                if err != nil && indef { err = cbor.NewReadError("indefinite maps not supported")}"#
1359            } else {
1360                r#"size,err := d.ReadMapSize()"#
1361            },
1362        )
1363        .unwrap();
1364        for field in fields.iter() {
1365            let (field_name, ser_name) = self.get_field_name_and_ser_name(field)?;
1366            writeln!(s, "case \"{ser_name}\":").unwrap();
1367            if is_optional_field(field, self.shape_kind(field.target())) {
1368                writeln!(
1369                    s,
1370                    r#"fval,err := {}
1371                  if err != nil {{ return val, err }}
1372                  val.{} = &fval"#,
1373                    &self.value_decoder(field.target(), DecodeRef::Plain, has_cbor)?,
1374                    &field_name,
1375                )
1376                .unwrap();
1377            } else {
1378                writeln!(
1379                    s,
1380                    r#"val.{},err = {}"#,
1381                    &field_name,
1382                    &self.value_decoder(field.target(), DecodeRef::Plain, has_cbor)?,
1383                )
1384                .unwrap();
1385            }
1386        }
1387        writeln!(
1388            s,
1389            r#" default: 
1390                err = d.Skip()
1391            }}
1392            if err != nil {{
1393                return val, err
1394            }}
1395            }}
1396            return val,nil"#,
1397        )
1398        .unwrap();
1399        Ok(s)
1400    }
1401
1402    /// Generates statements to encode the shape.
1403    fn shape_encoder(
1404        &self,
1405        id: &ShapeID,
1406        kind: &ShapeKind,
1407        val: &str,
1408        has_cbor: bool,
1409    ) -> Result<String> {
1410        let s = match kind {
1411            ShapeKind::Simple(simple) => match simple {
1412                Simple::Blob => encode_alias!(id, val, SHAPE_BLOB, "ByteArray", "[]byte"),
1413                Simple::Boolean => encode_alias!(id, val, SHAPE_BOOLEAN, "Bool", "boolean"),
1414                Simple::String => encode_alias!(id, val, SHAPE_STRING, "String", "string"),
1415                Simple::Byte => encode_alias!(id, val, SHAPE_BYTE, "Uint8", "uint8"),
1416                Simple::Short => encode_alias!(id, val, SHAPE_SHORT, "Uint16", "uint16"),
1417                Simple::Integer => encode_alias!(id, val, SHAPE_INTEGER, "Uint32", "uint32"),
1418                Simple::Long => encode_alias!(id, val, SHAPE_LONG, "Uint64", "uint64"),
1419                Simple::Float => encode_alias!(id, val, SHAPE_FLOAT, "Float32", "float32"),
1420                Simple::Double => encode_alias!(id, val, SHAPE_DOUBLE, "Float64", "float64"),
1421                Simple::Timestamp => {
1422                    format!(
1423                        "{}{}EncodeTimestamp(encoder,{}))",
1424                        &self.import_core,
1425                        codec_pfx(has_cbor),
1426                        val
1427                    )
1428                }
1429                Simple::Document => format!(
1430                    "{}.{}EncodeDocument({}))",
1431                    &self.import_core,
1432                    codec_pfx(has_cbor),
1433                    val
1434                ),
1435                Simple::BigInteger => todo!(),
1436                Simple::BigDecimal => todo!(),
1437            },
1438            ShapeKind::Map(map) => {
1439                // make sure key & val names are unique within scope of the current struct
1440                // (in case there are >1 map)
1441                let key_var = format!("key_{}", crate::strings::to_camel_case(val));
1442                let val_var = format!("val_{}", crate::strings::to_camel_case(val));
1443                format!(
1444                    r#"encoder.WriteMapSize(uint32(len(*{})))
1445                    for {},{} := range *{} {{
1446                        {}
1447                        {}
1448                    }}        
1449                    "#,
1450                    val,
1451                    &key_var,
1452                    &val_var,
1453                    val,
1454                    &self.value_encoder(map.key().target(), &key_var, "encoder", has_cbor,)?,
1455                    &self.value_encoder(map.value().target(), &val_var, "encoder", has_cbor,)?,
1456                )
1457            }
1458            ShapeKind::List(list) => {
1459                let item_var = format!("item_{}", crate::strings::to_camel_case(val));
1460                format!(
1461                    r#"
1462                    encoder.WriteArraySize(uint32(len(*{})))
1463                    for _,{} := range *{} {{
1464                        {}
1465                    }}
1466                    "#,
1467                    val,
1468                    &item_var,
1469                    val,
1470                    &self.value_encoder(list.member().target(), &item_var, "encoder", has_cbor,)?
1471                )
1472            }
1473            ShapeKind::Set(set) => {
1474                let item_var = format!("item_{}", crate::strings::to_camel_case(val));
1475                let mut s = format!(
1476                    r#"
1477                    encoder.WriteArraySize(len({}))
1478                    for _,{} := range {} {{
1479                    "#,
1480                    val, &item_var, val,
1481                );
1482                s.push_str(&self.value_encoder(
1483                    set.member().target(),
1484                    &item_var,
1485                    "encoder",
1486                    has_cbor,
1487                )?);
1488                s.push_str("\n}\n");
1489                s
1490            }
1491            ShapeKind::Structure(struct_) => {
1492                if id != crate::model::unit_shape() {
1493                    self.struct_encode(id, struct_, val, has_cbor)?
1494                } else {
1495                    "encoder.WriteNil()".to_string()
1496                }
1497            }
1498            ShapeKind::Union(_) => {
1499                todo!("unions not supported");
1500            }
1501            ShapeKind::Operation(_)
1502            | ShapeKind::Resource(_)
1503            | ShapeKind::Service(_)
1504            | ShapeKind::Unresolved => String::new(),
1505        };
1506        Ok(s)
1507    }
1508
1509    /// returns true if the decoder expects to be in a function and has a return statement
1510    fn is_decoder_function(&self, kind: &ShapeKind) -> bool {
1511        matches!(
1512            kind,
1513            ShapeKind::Map(_)
1514                | ShapeKind::List(_)
1515                | ShapeKind::Set(_)
1516                | ShapeKind::Structure(_)
1517                | ShapeKind::Union(_)
1518        )
1519    }
1520
1521    /// Generates statements to decode the shape.
1522    /// set 'return_val' true to force expression to include 'return' statement on simple types
1523    fn shape_decoder(
1524        &self,
1525        id: &ShapeID,
1526        kind: &ShapeKind,
1527        has_cbor: bool,
1528    ) -> Result<(String, ShapeID)> {
1529        let res = match kind {
1530            ShapeKind::Simple(simple) => match simple {
1531                Simple::Blob => (
1532                    "d.ReadByteArray()".into(),
1533                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_BLOB, None),
1534                ),
1535                Simple::Boolean => (
1536                    "d.ReadBool()".into(),
1537                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_BOOLEAN, None),
1538                ),
1539                Simple::String => (
1540                    "d.ReadString()".into(),
1541                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_STRING, None),
1542                ),
1543                Simple::Byte => (
1544                    "d.ReadUint8()".into(),
1545                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_BYTE, None),
1546                ),
1547                Simple::Short => (
1548                    "d.ReadUint16()".into(),
1549                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_SHORT, None),
1550                ),
1551                Simple::Integer => (
1552                    "d.ReadUint32()".into(),
1553                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_INTEGER, None),
1554                ),
1555
1556                Simple::Long => (
1557                    "d.ReadUint64()".into(),
1558                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_LONG, None),
1559                ),
1560                Simple::Float => (
1561                    "d.ReadFloat32()".into(),
1562                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_FLOAT, None),
1563                ),
1564                Simple::Double => (
1565                    "d.ReadFloat64()".into(),
1566                    ShapeID::new_unchecked(PRELUDE_NAMESPACE, SHAPE_DOUBLE, None),
1567                ),
1568                Simple::Timestamp => (
1569                    format!("{}Timestamp.Encode()", &self.import_core),
1570                    id.clone(),
1571                ),
1572                Simple::Document => (
1573                    format!("{}Document.Encode()", &self.import_core),
1574                    id.clone(),
1575                ),
1576                Simple::BigInteger => todo!(),
1577                Simple::BigDecimal => todo!(),
1578            },
1579            ShapeKind::Map(map) => {
1580                let key_type = self.type_string(Ty::Shape(map.key().target()))?;
1581                let val_type = self.type_string(Ty::Shape(map.value().target()))?;
1582                (
1583                    format!(
1584                        r#"isNil,err := d.IsNextNil()
1585                        if err != nil || isNil {{
1586                       		return make(map[{}]{}, 0), err
1587                        }}
1588                       	{}
1589                        if err != nil {{ return make(map[{}]{}, 0),err }}
1590                        val := make(map[{}]{}, size)
1591                        for i := uint32(0); i < size; i++ {{
1592                           k,_ := {}
1593                           v,err := {}
1594                           if err != nil {{ return val, err }}
1595                           val[k] = v
1596                        }}
1597                        return val,nil"#,
1598                        &key_type,
1599                        &val_type,
1600                        if has_cbor {
1601                            r#"size,indef,err := d.ReadMapSize()
1602                if err != nil && indef { err = cbor.NewReadError("indefinite maps not supported") }"#
1603                        } else {
1604                            r#"size,err := d.ReadMapSize()"#
1605                        },
1606                        &key_type,
1607                        &val_type,
1608                        &key_type,
1609                        &val_type,
1610                        &self.value_decoder(map.key().target(), DecodeRef::Plain, has_cbor)?,
1611                        &self.value_decoder(map.value().target(), DecodeRef::Plain, has_cbor)?,
1612                    ),
1613                    id.clone(),
1614                )
1615            }
1616            ShapeKind::List(list) => {
1617                let item_type = self.type_string(Ty::Shape(list.member().target()))?;
1618                (
1619                    format!(
1620                        r#"isNil,err := d.IsNextNil()
1621                        if err != nil || isNil {{
1622                       		return make([]{}, 0), err
1623                        }}
1624                       	{}
1625                        if err != nil {{ return make([]{}, 0 ), err }}
1626                        val := make([]{}, 0, size)
1627                        for i := uint32(0); i < size; i++ {{
1628                           item,err := {}
1629                           if err != nil {{ return val, err }}
1630                           val = append(val,item)
1631                        }}
1632                        return val,nil"#,
1633                        &item_type,
1634                        if has_cbor {
1635                            r#"size,indef,err := d.ReadArraySize()
1636                if err != nil && indef { err = cbor.NewReadError("indefinite arrays not supported") }"#
1637                        } else {
1638                            r#"size,err := d.ReadArraySize()"#
1639                        },
1640                        &item_type,
1641                        &item_type,
1642                        &self.value_decoder(list.member().target(), DecodeRef::Plain, has_cbor)?,
1643                    ),
1644                    id.clone(),
1645                )
1646            }
1647            ShapeKind::Set(set) => {
1648                let item_type = self.to_type_name_case(&set.member().target().to_string());
1649                (
1650                    format!(
1651                        r#"isNil,err := d.IsNextNil()
1652                        if err != nil || isNil {{
1653                       		return make([]{}, 0), err
1654                        }}
1655                       	{}
1656                        if err != nil {{ return make([]{},0),err }}
1657                        val := make([]{}, 0, size)
1658                        for i := uint32(0); i < size; i++ {{
1659                           item,err := {}
1660                           if err != nil {{ return val, err }}
1661                           val = append(val,item)
1662                        }}
1663                        return val,nil"#,
1664                        &item_type,
1665                        if has_cbor {
1666                            r#"size,indef,err := d.ReadArraySize()
1667                        if err != nil && indef { err = cbor.NewReadError("indefinite arrays not supported")}"#
1668                        } else {
1669                            r#"size,err := d.ReadArraySize()"#
1670                        },
1671                        &item_type,
1672                        &item_type,
1673                        &self.value_decoder(set.member().target(), DecodeRef::Plain, has_cbor)?,
1674                    ),
1675                    id.clone(),
1676                )
1677            }
1678            ShapeKind::Structure(struct_) => {
1679                if id != crate::model::unit_shape() {
1680                    (self.struct_decode(id, struct_, has_cbor)?, id.clone())
1681                } else {
1682                    (
1683                        r#"_ = d.Skip()
1684                        return Unit{},nil"#
1685                            .into(),
1686                        id.clone(),
1687                    )
1688                }
1689            }
1690            ShapeKind::Union(_) => {
1691                todo!("unions not supported");
1692            }
1693            ShapeKind::Operation(_)
1694            | ShapeKind::Resource(_)
1695            | ShapeKind::Service(_)
1696            | ShapeKind::Unresolved => (String::new(), id.clone()),
1697        };
1698        Ok(res)
1699    }
1700
1701    /// write statement(s) to encode an object
1702    pub(crate) fn value_encoder(
1703        &self,
1704        id: &ShapeID,
1705        val: &str,
1706        e: &str,
1707        has_cbor: bool,
1708    ) -> Result<String> {
1709        let name = id.shape_name().to_string();
1710        let serde_fn = codec_pfx(has_cbor);
1711        let stmt = if id.namespace() == prelude_namespace_id() {
1712            match name.as_ref() {
1713                SHAPE_BLOB => format!("{e}.WriteByteArray({val})"),
1714                SHAPE_BOOLEAN | SHAPE_PRIMITIVEBOOLEAN => format!("{e}.WriteBool({val})"),
1715                SHAPE_STRING => format!("{e}.WriteString({val})"),
1716                SHAPE_BYTE | SHAPE_PRIMITIVEBYTE => format!("{e}.WriteUint8({val})"),
1717                SHAPE_SHORT | SHAPE_PRIMITIVESHORT => format!("{e}.WriteUint16({val})"),
1718                SHAPE_INTEGER | SHAPE_PRIMITIVEINTEGER => {
1719                    format!("{e}.WriteUint32({val})")
1720                }
1721                SHAPE_LONG | SHAPE_PRIMITIVELONG => format!("{e}.WriteUint64({val})"),
1722                SHAPE_FLOAT | SHAPE_PRIMITIVEFLOAT => format!("{e}.WriteFloat32({val})"),
1723                SHAPE_DOUBLE | SHAPE_PRIMITIVEDOUBLE => {
1724                    format!("{e}.WriteFloat64({val})")
1725                }
1726                SHAPE_TIMESTAMP => format!("{val}.{serde_fn}Encode({e})"),
1727                //SHAPE_DOCUMENT => todo!(),
1728                //SHAPE_BIGINTEGER => todo!(),
1729                //SHAPE_BIGDECIMAL => todo!(),
1730                _ => return Err(Error::UnsupportedType(name)),
1731            }
1732        } else if id.namespace() == wasmcloud_model_namespace() {
1733            match name.as_bytes() {
1734                b"U64" => format!("{e}.WriteUint64({val})"),
1735                b"U32" => format!("{e}.WriteUint32({val})"),
1736                b"U16" => format!("{e}.WriteUint16({val})"),
1737                b"U8" => format!("{e}.WriteUint8({val})"),
1738                b"I64" => format!("{e}.WriteInt64({val})"),
1739                b"I32" => format!("{e}.WriteInt32({val})"),
1740                b"I16" => format!("{e}.WriteInt16({val})"),
1741                b"I8" => format!("{e}.WriteInt8({val})"),
1742                b"F64" => format!("{e}.WriteFloat64({val})"),
1743                b"F32" => format!("{e}.WriteFloat32({val})"),
1744                _ => format!("{val}.{serde_fn}Encode({e})",),
1745            }
1746        } else {
1747            format!("{val}.{serde_fn}Encode({e})")
1748        };
1749        Ok(stmt)
1750    }
1751
1752    /// write statement(s) to decode an object
1753    pub(crate) fn value_decoder(
1754        &self,
1755        id: &ShapeID,
1756        d_byref: DecodeRef,
1757        has_cbor: bool,
1758    ) -> Result<String> {
1759        let name = id.shape_name().to_string();
1760        let stmt = if id.namespace() == prelude_namespace_id() {
1761            match name.as_ref() {
1762                SHAPE_BLOB => "d.ReadByteArray()".into(),
1763                SHAPE_BOOLEAN | SHAPE_PRIMITIVEBOOLEAN => "d.ReadBool()".into(),
1764                SHAPE_STRING => "d.ReadString()".into(),
1765                SHAPE_BYTE | SHAPE_PRIMITIVEBYTE => "d.ReadUint8()".into(),
1766                SHAPE_SHORT | SHAPE_PRIMITIVESHORT => "d.ReadUint16()".into(),
1767                SHAPE_INTEGER | SHAPE_PRIMITIVEINTEGER => "d.ReadUint32()".into(),
1768                SHAPE_LONG | SHAPE_PRIMITIVELONG => "d.ReadUint64()".into(),
1769                SHAPE_FLOAT | SHAPE_PRIMITIVEFLOAT => "d.ReadFloat32()".into(),
1770                SHAPE_DOUBLE | SHAPE_PRIMITIVEDOUBLE => "d.ReadFloat64()".into(),
1771                SHAPE_TIMESTAMP => format!(
1772                    "{}{}DecodeTimestamp({})",
1773                    &self.import_core,
1774                    codec_pfx(has_cbor),
1775                    &d_byref
1776                ),
1777                SHAPE_DOCUMENT => format!(
1778                    "{}{}DecodeDocument({})",
1779                    &self.import_core,
1780                    codec_pfx(has_cbor),
1781                    &d_byref
1782                ),
1783                //SHAPE_BIGINTEGER => todo!(),
1784                //SHAPE_BIGDECIMAL => todo!(),
1785                _ => return Err(Error::UnsupportedType(name)),
1786            }
1787        } else if id.namespace() == wasmcloud_model_namespace() {
1788            match name.as_bytes() {
1789                b"U64" => "d.ReadUint64()".into(),
1790                b"U32" => "d.ReadUint32()".into(),
1791                b"U16" => "d.ReadUint16()".into(),
1792                b"U8" => "d.ReadUint8()".into(),
1793                b"I64" => "d.ReadInt64()".into(),
1794                b"I32" => "d.ReadInt32()".into(),
1795                b"I16" => "d.ReadInt16()".into(),
1796                b"I8" => "d.ReadInt8()".into(),
1797                b"F64" => "d.ReadFloat64()".into(),
1798                b"F32" => "d.ReadFloat32()".into(),
1799                _ => format!(
1800                    "{}{}Decode{}({})",
1801                    &self.import_core,
1802                    codec_pfx(has_cbor),
1803                    crate::strings::to_pascal_case(&id.shape_name().to_string()),
1804                    &d_byref,
1805                ),
1806            }
1807        } else {
1808            format!(
1809                "{}{}Decode{}({})",
1810                self.get_crate_path(id)?,
1811                codec_pfx(has_cbor),
1812                crate::strings::to_pascal_case(&id.shape_name().to_string()),
1813                &d_byref,
1814            )
1815        };
1816        Ok(stmt)
1817    }
1818
1819    fn shape_kind(&self, id: &ShapeID) -> Option<&ShapeKind> {
1820        self.model.unwrap().shape(id).map(|ts| ts.body())
1821    }
1822} // impl GoCodeGen
1823
1824/// is_optional_type determines whether the field should be declared as *Field in its struct.
1825/// the value is true if it is nillable and either isn't required or has an explicit `box` trait
1826pub(crate) fn is_optional_field(field: &MemberShape, kind: Option<&ShapeKind>) -> bool {
1827    (field.is_boxed() || !field.is_required()) && zero_of(field.target(), kind) == "nil"
1828}
1829
1830/*
1831Opt   @required   @box    bool/int/...
18321     0           0       0
18330     0           0       1
18341     0           1       0
18351     0           1       1
18360     1           0       0
18370     1           0       1
1838x     1           1       0
1839x     1           1       1
1840*/
1841
1842// check that the codegen package has a parseable version
1843#[test]
1844fn package_semver() {
1845    let package_version = env!("CARGO_PKG_VERSION");
1846    let version = semver::Version::parse(package_version);
1847    assert!(
1848        version.is_ok(),
1849        "package version {package_version} has unexpected format"
1850    );
1851}
1852
1853pub struct GoSourceFormatter {
1854    pub program: String,
1855    pub args: Vec<String>,
1856}
1857
1858impl SourceFormatter for GoSourceFormatter {
1859    fn run(&self, source_files: &[&str]) -> Result<()> {
1860        // we get an error if the files are in different packages,
1861        // so run once per file in case output packages differ
1862        for f in source_files {
1863            // TODO(future): caller converts array of paths to array of str, and we convert back to path again.
1864            // ... we could change the api to this fn to take array of Path or PathBuf instead
1865            let mut args = self.args.clone();
1866            let path = std::fs::canonicalize(f)?;
1867            args.push(path.to_string_lossy().to_string());
1868            let str_args: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
1869            if let Err(e) = format::run_command(&self.program, &str_args) {
1870                eprintln!("Warning:  formatting '{}': {}", path.display(), e);
1871            }
1872        }
1873        Ok(())
1874    }
1875}