1use std::{fmt, ops::Deref, str::FromStr};
7
8use atelier_core::{
9 model::{
10 shapes::{AppliedTraits, HasTraits, MemberShape, Operation, ShapeKind, StructureOrUnion},
11 values::{Number, Value as NodeValue},
12 HasIdentity, Identifier, Model, NamespaceID, ShapeID,
13 },
14 prelude::prelude_namespace_id,
15};
16use lazy_static::lazy_static;
17use serde::{de::DeserializeOwned, Deserialize};
18
19use crate::{
20 error::{Error, Result},
21 JsonValue,
22};
23
24const WASMCLOUD_MODEL_NAMESPACE: &str = "org.wasmcloud.model";
25const WASMCLOUD_CORE_NAMESPACE: &str = "org.wasmcloud.core";
26const WASMCLOUD_ACTOR_NAMESPACE: &str = "org.wasmcloud.actor";
27
28const TRAIT_CODEGEN_RUST: &str = "codegenRust";
29const TRAIT_SERIALIZATION: &str = "serialization";
35const TRAIT_WASMBUS: &str = "wasmbus";
36const TRAIT_WASMBUS_DATA: &str = "wasmbusData";
37const TRAIT_FIELD_NUM: &str = "n";
38const TRAIT_RENAME: &str = "rename";
39
40lazy_static! {
41 static ref WASMCLOUD_MODEL_NAMESPACE_ID: NamespaceID =
42 NamespaceID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE);
43 static ref WASMCLOUD_CORE_NAMESPACE_ID: NamespaceID =
44 NamespaceID::new_unchecked(WASMCLOUD_CORE_NAMESPACE);
45 static ref WASMCLOUD_ACTOR_NAMESPACE_ID: NamespaceID =
46 NamespaceID::new_unchecked(WASMCLOUD_ACTOR_NAMESPACE);
47 static ref SERIALIZATION_TRAIT_ID: ShapeID = ShapeID::new(
48 NamespaceID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE),
49 Identifier::from_str(TRAIT_SERIALIZATION).unwrap(),
50 None
51 );
52 static ref CODEGEN_RUST_TRAIT_ID: ShapeID = ShapeID::new(
53 NamespaceID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE),
54 Identifier::from_str(TRAIT_CODEGEN_RUST).unwrap(),
55 None
56 );
57 static ref WASMBUS_TRAIT_ID: ShapeID = ShapeID::new(
58 NamespaceID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE),
59 Identifier::from_str(TRAIT_WASMBUS).unwrap(),
60 None
61 );
62 static ref WASMBUS_DATA_TRAIT_ID: ShapeID = ShapeID::new(
63 NamespaceID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE),
64 Identifier::from_str(TRAIT_WASMBUS_DATA).unwrap(),
65 None
66 );
67 static ref FIELD_NUM_TRAIT_ID: ShapeID = ShapeID::new(
68 NamespaceID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE),
69 Identifier::from_str(TRAIT_FIELD_NUM).unwrap(),
70 None
71 );
72 static ref RENAME_TRAIT_ID: ShapeID = ShapeID::new(
73 NamespaceID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE),
74 Identifier::from_str(TRAIT_RENAME).unwrap(),
75 None
76 );
77 static ref UNIT_ID: ShapeID = ShapeID::new_unchecked(WASMCLOUD_MODEL_NAMESPACE, "Unit", None);
78}
79
80pub fn wasmcloud_model_namespace() -> &'static NamespaceID {
82 &WASMCLOUD_MODEL_NAMESPACE_ID
83}
84pub fn wasmcloud_core_namespace() -> &'static NamespaceID {
85 &WASMCLOUD_CORE_NAMESPACE_ID
86}
87pub fn wasmcloud_actor_namespace() -> &'static NamespaceID {
88 &WASMCLOUD_ACTOR_NAMESPACE_ID
89}
90
91#[cfg(feature = "wasmbus")]
92pub fn wasmbus_trait() -> &'static ShapeID {
94 &WASMBUS_TRAIT_ID
95}
96
97#[allow(dead_code)]
98#[cfg(feature = "wasmbus")]
99pub fn wasmbus_data_trait() -> &'static ShapeID {
101 &WASMBUS_DATA_TRAIT_ID
102}
103
104pub fn serialization_trait() -> &'static ShapeID {
106 &SERIALIZATION_TRAIT_ID
107}
108
109pub fn codegen_rust_trait() -> &'static ShapeID {
111 &CODEGEN_RUST_TRAIT_ID
112}
113
114pub fn field_num_trait() -> &'static ShapeID {
116 &FIELD_NUM_TRAIT_ID
117}
118
119pub fn rename_trait() -> &'static ShapeID {
121 &RENAME_TRAIT_ID
122}
123
124pub fn unit_shape() -> &'static ShapeID {
125 &UNIT_ID
126}
127
128#[allow(dead_code)]
129pub enum CommentKind {
130 Inner,
131 Documentation,
132 InQuote,
134}
135
136#[derive(Default, Clone, PartialEq, Eq)]
137pub struct WasmbusProtoVersion {
138 base: u8, }
140
141impl TryFrom<&str> for WasmbusProtoVersion {
142 type Error = crate::error::Error;
143
144 fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
145 match value {
146 "0" => Ok(WasmbusProtoVersion { base: 0 }),
147 "2" => Ok(WasmbusProtoVersion { base: 2 }),
148 _ => Err(Error::Model(format!(
149 "Invalid wasmbus.protocol: '{value}'. The default value is \"0\"."
150 ))),
151 }
152 }
153}
154
155impl fmt::Debug for WasmbusProtoVersion {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 f.write_str(&self.to_string())
158 }
159}
160
161impl ToString for WasmbusProtoVersion {
162 fn to_string(&self) -> String {
163 format!("{}", self.base)
164 }
165}
166
167impl WasmbusProtoVersion {
168 pub fn has_cbor(&self) -> bool {
169 self.base >= 2
170 }
171}
172
173pub(crate) enum Ty<'typ> {
178 Shape(&'typ ShapeID),
180 Opt(&'typ ShapeID),
182 Ref(&'typ ShapeID),
184
185 Ptr(&'typ ShapeID),
187}
188
189#[macro_export]
191macro_rules! expect_empty {
192 ($list:expr, $msg:expr) => {
193 if !$list.is_empty() {
194 return Err(Error::InvalidModel(format!(
195 "{}: {}",
196 $msg,
197 $list.keys().map(|k| k.to_string()).collect::<Vec<String>>().join(",")
198 )));
199 }
200 };
201}
202
203#[macro_export]
204macro_rules! unsupported_shape {
205 ($fn_name:ident, $shape_type:ty, $doc:expr) => {
206 #[allow(unused_variables)]
207 fn $fn_name(
208 &mut self,
209 id: &ShapeID,
210 traits: &AppliedTraits,
211 shape: &$shape_type,
212 ) -> Result<()> {
213 return Err(weld_codegen::error::Error::UnsupportedShape(
214 id.to_string(),
215 $doc.to_string(),
216 ));
217 }
218 };
219}
220
221pub fn is_opt_namespace(id: &ShapeID, ns: &Option<NamespaceID>) -> bool {
223 match ns {
224 Some(ns) => id.namespace() == ns,
225 None => true,
226 }
227}
228
229pub fn get_operation<'model>(
231 model: &'model Model,
232 operation_id: &'_ ShapeID,
233 service_id: &'_ Identifier,
234) -> Result<(&'model Operation, &'model AppliedTraits)> {
235 let op = model
236 .shapes()
237 .filter(|t| t.id() == operation_id)
238 .find_map(|t| {
239 if let ShapeKind::Operation(op) = t.body() {
240 Some((op, t.traits()))
241 } else {
242 None
243 }
244 })
245 .ok_or_else(|| {
246 Error::Model(format!(
247 "missing operation {} for service {}",
248 &operation_id.to_string(),
249 &service_id.to_string()
250 ))
251 })?;
252 Ok(op)
253}
254
255pub fn get_trait<T: DeserializeOwned>(traits: &AppliedTraits, id: &ShapeID) -> Result<Option<T>> {
258 match traits.get(id) {
259 Some(Some(val)) => match trait_value(val) {
260 Ok(obj) => Ok(Some(obj)),
261 Err(e) => Err(e),
262 },
263 Some(None) => Ok(None),
264 None => Ok(None),
265 }
266}
267
268pub fn wasmbus_proto(traits: &AppliedTraits) -> Result<Option<WasmbusProtoVersion>> {
272 match get_trait(traits, wasmbus_trait()) {
273 Ok(Some(Wasmbus { protocol: Some(version), .. })) => {
274 Ok(Some(WasmbusProtoVersion::try_from(version.as_str())?))
275 }
276 Ok(_) => Ok(Some(WasmbusProtoVersion::default())),
277 _ => Ok(None),
278 }
279}
280
281pub fn trait_value<T: DeserializeOwned>(value: &NodeValue) -> Result<T> {
283 let json = value_to_json(value);
284 let obj = serde_json::from_value(json)?;
285 Ok(obj)
286}
287
288pub fn value_to_json(value: &NodeValue) -> JsonValue {
290 match value {
291 NodeValue::None => JsonValue::Null,
292 NodeValue::Array(v) => JsonValue::Array(v.iter().map(value_to_json).collect()),
293 NodeValue::Object(v) => {
294 let mut object = crate::JsonMap::default();
295 for (k, v) in v {
296 let _ = object.insert(k.clone(), value_to_json(v));
297 }
298 JsonValue::Object(object)
299 }
300 NodeValue::Number(v) => match v {
301 Number::Integer(v) => JsonValue::Number((*v).into()),
302 Number::Float(v) => JsonValue::Number(serde_json::Number::from_f64(*v).unwrap()),
303 },
304 NodeValue::Boolean(v) => JsonValue::Bool(*v),
305 NodeValue::String(v) => JsonValue::String(v.clone()),
306 }
307}
308
309pub fn resolve<'model>(model: &'model Model, shape: &'model ShapeID) -> &'model ShapeID {
313 if let Some(resolved) = model.shape(shape) {
314 resolved.id()
315 } else {
316 shape
317 }
318}
319
320pub fn has_default(model: &'_ Model, member: &MemberShape) -> bool {
333 let id = resolve(model, member.target());
334 #[allow(unused_mut)]
335 let mut has = false;
336 let name = id.shape_name().to_string();
337
338 if id.namespace().eq(prelude_namespace_id()) {
339 cfg_if::cfg_if! {
340 if #[cfg(feature = "BigInteger")] {
341 has = has || &name == "bigInteger";
342 }
343 }
344 cfg_if::cfg_if! {
345 if #[cfg(feature = "BigDecimal")] {
346 has = has || &name == "bigDecimal";
347 }
348 }
349 has || matches!(
350 name.as_str(),
351 "List" | "Set" | "Map"
353 | "Blob" | "Boolean" | "String" | "Byte" | "Short"
355 | "Integer" | "Long" | "Float" | "Double"
356 | "Timestamp"
357 )
358 } else if id.namespace() == wasmcloud_model_namespace() {
360 matches!(
361 name.as_str(),
362 "U64" | "U32" | "U16" | "U8" | "I64" | "I32" | "I16" | "I8" | "F64" | "F32"
363 )
364 } else {
365 false
366 }
370}
371
372pub struct NumberedMember {
373 field_num: Option<u16>,
374 shape: MemberShape,
375}
376
377impl NumberedMember {
378 pub(crate) fn new(member: &MemberShape) -> Result<Self> {
379 Ok(NumberedMember {
380 shape: member.to_owned(),
381 field_num: get_trait::<u16>(member.traits(), field_num_trait()).map_err(|e| {
382 Error::Model(format!(
383 "invalid field number @n() for field '{}': {}",
384 member.id(),
385 e
386 ))
387 })?,
388 })
389 }
390
391 pub(crate) fn field_num(&self) -> &Option<u16> {
392 &self.field_num
393 }
394}
395
396impl Deref for NumberedMember {
397 type Target = MemberShape;
398
399 fn deref(&self) -> &Self::Target {
400 &self.shape
401 }
402}
403
404use std::iter::Iterator;
405
406use crate::wasmbus_model::Wasmbus;
407
408pub(crate) fn get_sorted_fields(
411 id: &Identifier,
412 strukt: &StructureOrUnion,
413) -> Result<(Vec<NumberedMember>, bool)> {
414 let mut fields = strukt
415 .members()
416 .map(NumberedMember::new)
417 .collect::<Result<Vec<NumberedMember>>>()?;
418 let has_numbers = crate::model::has_field_numbers(&fields, &id.to_string())?;
419 if has_numbers {
422 fields.sort_by_key(|f| f.field_num().unwrap());
423 } else {
424 fields.sort_by_key(|f| f.id().to_owned());
425 }
426 Ok((fields, has_numbers))
427}
428
429pub(crate) fn has_field_numbers(fields: &[NumberedMember], name: &str) -> Result<bool> {
434 let mut numbered = std::collections::BTreeSet::default();
435 for f in fields.iter() {
436 if let Some(n) = f.field_num() {
437 numbered.insert(*n);
438 }
439 }
440 if numbered.is_empty() {
441 Ok(false)
442 } else if numbered.len() == fields.len() {
443 Ok(true)
445 } else {
446 Err(crate::Error::Model(format!(
447 "structure {name} has incomplete or invalid field numbers: either some fields are missing \
448 the '@n()' trait, or some fields have duplicate numbers."
449 )))
450 }
451}
452
453#[derive(Clone, Deserialize)]
467pub struct PackageName {
468 pub namespace: String,
469 #[serde(rename = "crate")]
470 pub crate_name: Option<String>,
471 #[serde(rename = "py_module")]
472 pub py_module: Option<String>,
473 pub go_package: Option<String>,
474 pub doc: Option<String>,
475}