1use 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#[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
54fn 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 }
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 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 }
132
133pub fn zero_of(id: &ShapeID, kind: Option<&ShapeKind>) -> &'static str {
135 if let Some(ShapeKind::Simple(simple)) = kind {
136 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 SHAPE_BLOB => "make([]byte,0)",
160 SHAPE_STRING => "\"\"",
161 _ => "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
174pub 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 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(¤t_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 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 #[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 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 ""
358 }
359 )
360 .unwrap();
361 Ok(())
362 }
363
364 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 shapes.sort_by_key(|v| v.0);
385
386 for (id, traits, shape) in shapes.into_iter() {
387 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 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 }
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 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 fn write_comment(&mut self, w: &mut Writer, _kind: CommentKind, line: &str) {
470 writeln!(w, "// {line}").unwrap();
472 }
473
474 fn to_method_name_case(&self, name: &str) -> String {
476 crate::strings::to_pascal_case(name)
477 }
478
479 fn to_field_name_case(&self, name: &str) -> String {
481 crate::strings::to_pascal_case(name)
482 }
483
484 fn to_type_name_case(&self, name: &str) -> String {
486 crate::strings::to_pascal_case(name)
487 }
488
489 fn get_file_extension(&self) -> &'static str {
491 "go"
492 }
493}
494
495macro_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 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 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)), 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 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 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 if traits.get(&prelude_shape_named(TRAIT_UNSTABLE).unwrap()).is_some() {
611 self.write_comment(w, CommentKind::Documentation, "@unstable");
612 }
613 }
614
615 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!()
650 }
651 SHAPE_BIGDECIMAL => {
652 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 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 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!()
721 }
722 Simple::BigDecimal => {
723 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[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 #[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 service_id,
1239 contract,
1240 service_id,
1241 service_id,
1242 &self.import_core,
1243 contract,
1244 service_id,
1245 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 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 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 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 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 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 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 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 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 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 _ => 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 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 _ => 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} pub(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#[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 for f in source_files {
1863 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}