1use borsh::schema::{
2 BorshSchemaContainer, Declaration, Definition, DiscriminantValue, Fields, VariantName,
3};
4use schemars::schema::{RootSchema, Schema};
5use schemars::JsonSchema;
6use semver::Version;
7use serde::{de, Deserialize, Deserializer, Serialize};
8use std::collections::{BTreeMap, HashMap};
9
10#[doc(hidden)]
11#[cfg(feature = "__chunked-entries")]
12#[path = "private.rs"]
13pub mod __private;
14
15const SCHEMA_SEMVER: Version = Version {
17 major: 0,
18 minor: 4,
19 patch: 0,
20 pre: semver::Prerelease::EMPTY,
21 build: semver::BuildMetadata::EMPTY,
22};
23
24pub const SCHEMA_VERSION: &str = "0.4.0";
26
27#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, JsonSchema)]
29#[serde(deny_unknown_fields)]
30pub struct AbiRoot {
31 #[serde(deserialize_with = "ensure_current_version")]
33 pub schema_version: String,
34 pub metadata: AbiMetadata,
36 pub body: AbiBody,
38}
39
40fn ensure_current_version<'de, D: Deserializer<'de>>(d: D) -> Result<String, D::Error> {
41 let unchecked = String::deserialize(d)?;
42 let version = Version::parse(&unchecked)
43 .map_err(|_| de::Error::custom("expected `schema_version` to be a valid semver value"))?;
44 if version.major != SCHEMA_SEMVER.major || version.minor != SCHEMA_SEMVER.minor {
45 if version < SCHEMA_SEMVER {
46 return Err(de::Error::custom(format!(
47 "expected `schema_version` to be ~{}.{}, but got {}: consider re-generating your ABI file with a newer version of SDK and cargo-unc",
48 SCHEMA_SEMVER.major, SCHEMA_SEMVER.minor, version
49 )));
50 } else {
51 return Err(de::Error::custom(format!(
52 "expected `schema_version` to be ~{}.{}, but got {}: consider upgrading unc-abi to a newer version",
53 SCHEMA_SEMVER.major, SCHEMA_SEMVER.minor, version
54 )));
55 }
56 }
57 Ok(unchecked)
58}
59
60#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Default, JsonSchema)]
61pub struct BuildInfo {
62 pub compiler: String,
64 pub builder: String,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub image: Option<String>,
69}
70
71#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Default, JsonSchema)]
72pub struct AbiMetadata {
73 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub name: Option<String>,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
78 pub version: Option<String>,
79 #[serde(default, skip_serializing_if = "Vec::is_empty")]
81 pub authors: Vec<String>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
84 pub build: Option<BuildInfo>,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub wasm_hash: Option<String>,
88 #[serde(default, flatten, skip_serializing_if = "HashMap::is_empty")]
90 pub other: HashMap<String, String>,
91}
92
93#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
95#[serde(deny_unknown_fields)]
96pub struct AbiBody {
97 pub functions: Vec<AbiFunction>,
99 pub root_schema: RootSchema,
101}
102
103#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, JsonSchema)]
105#[serde(deny_unknown_fields)]
106pub struct AbiFunction {
107 pub name: String,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub doc: Option<String>,
111 pub kind: AbiFunctionKind,
113 #[serde(default, skip_serializing_if = "Vec::is_empty")]
115 pub modifiers: Vec<AbiFunctionModifier>,
116 #[serde(default, skip_serializing_if = "AbiParameters::is_empty")]
118 pub params: AbiParameters,
119 #[serde(default, skip_serializing_if = "Vec::is_empty")]
121 pub callbacks: Vec<AbiType>,
122 #[serde(default, skip_serializing_if = "Option::is_none")]
124 pub callbacks_vec: Option<AbiType>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub result: Option<AbiType>,
128}
129
130#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, JsonSchema)]
133#[serde(rename_all = "lowercase")]
134pub enum AbiFunctionKind {
135 View,
136 Call,
137}
138
139#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, JsonSchema)]
141#[serde(rename_all = "lowercase")]
142pub enum AbiFunctionModifier {
143 Init,
145 Private,
149 Payable,
153}
154
155#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, JsonSchema)]
157#[serde(tag = "serialization_type")]
158#[serde(rename_all = "lowercase")]
159#[serde(deny_unknown_fields)]
160pub enum AbiParameters {
161 Json { args: Vec<AbiJsonParameter> },
162 Borsh { args: Vec<AbiBorshParameter> },
163}
164
165impl Default for AbiParameters {
166 fn default() -> Self {
167 AbiParameters::Json { args: Vec::new() }
170 }
171}
172
173impl AbiParameters {
174 pub fn is_empty(&self) -> bool {
175 match self {
176 Self::Json { args } => args.is_empty(),
177 Self::Borsh { args } => args.is_empty(),
178 }
179 }
180}
181
182#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, JsonSchema)]
184#[serde(deny_unknown_fields)]
185pub struct AbiJsonParameter {
186 pub name: String,
188 pub type_schema: Schema,
190}
191
192#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
194#[serde(deny_unknown_fields)]
195pub struct AbiBorshParameter {
196 pub name: String,
198 #[serde(with = "BorshSchemaContainerDef")]
200 pub type_schema: BorshSchemaContainer,
201}
202
203impl JsonSchema for AbiBorshParameter {
204 fn schema_name() -> String {
205 "AbiBorshParameter".to_string()
206 }
207
208 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> Schema {
209 let mut name_schema_object = <String as JsonSchema>::json_schema(gen).into_object();
210 name_schema_object.metadata().description =
211 Some("Parameter name (e.g. `p1` in `fn foo(p1: u32) {}`).".to_string());
212
213 let mut type_schema_object = Schema::Bool(true).into_object();
214 type_schema_object.metadata().description =
215 Some("Inline Borsh schema that represents this type.".to_string());
216
217 let mut schema_object = schemars::schema::SchemaObject {
218 instance_type: Some(schemars::schema::InstanceType::Object.into()),
219 ..Default::default()
220 };
221 schema_object.metadata().description =
222 Some("Information about a single named Borsh function parameter.".to_string());
223 let object_validation = schema_object.object();
224 object_validation
225 .properties
226 .insert("name".to_string(), name_schema_object.into());
227 object_validation
228 .properties
229 .insert("type_schema".to_string(), type_schema_object.into());
231 object_validation.required.insert("name".to_string());
232 object_validation.required.insert("type_schema".to_string());
233 object_validation.additional_properties =
234 Some(schemars::schema::Schema::Bool(false).into());
235 schema_object.into()
236 }
237}
238
239#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
241#[serde(tag = "serialization_type")]
242#[serde(rename_all = "lowercase")]
243#[serde(deny_unknown_fields)]
244pub enum AbiType {
245 Json {
246 type_schema: Schema,
248 },
249 Borsh {
250 #[serde(with = "BorshSchemaContainerDef")]
252 type_schema: BorshSchemaContainer,
253 },
254}
255
256impl JsonSchema for AbiType {
257 fn schema_name() -> String {
258 "AbiType".to_string()
259 }
260
261 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> Schema {
262 let mut json_abi_type = schemars::schema::SchemaObject::default();
263 let json_abi_schema = json_abi_type.object();
264 json_abi_schema
265 .properties
266 .insert("serialization_type".to_string(), {
267 let schema = <String as JsonSchema>::json_schema(gen);
268 let mut schema = schema.into_object();
269 schema.enum_values = Some(vec!["json".into()]);
270 schema.into()
271 });
272 json_abi_schema
273 .properties
274 .insert("type_schema".to_string(), gen.subschema_for::<Schema>());
275 json_abi_schema
276 .required
277 .insert("serialization_type".to_string());
278 json_abi_schema.required.insert("type_schema".to_string());
279 json_abi_schema.additional_properties = Some(schemars::schema::Schema::Bool(false).into());
280
281 let mut borsh_abi_type = schemars::schema::SchemaObject::default();
282 let borsh_abi_schema = borsh_abi_type.object();
283 borsh_abi_schema
284 .properties
285 .insert("serialization_type".to_string(), {
286 let schema = <String as JsonSchema>::json_schema(gen);
287 let mut schema = schema.into_object();
288 schema.enum_values = Some(vec!["borsh".into()]);
289 schema.into()
290 });
291 borsh_abi_schema
292 .properties
293 .insert(
295 "type_schema".to_string(),
296 schemars::schema::SchemaObject::default().into(),
297 );
298 borsh_abi_schema
299 .required
300 .insert("serialization_type".to_string());
301 borsh_abi_schema.required.insert("type_schema".to_string());
302 borsh_abi_schema.additional_properties = Some(schemars::schema::Schema::Bool(false).into());
303
304 let mut schema_object = schemars::schema::SchemaObject {
305 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
306 one_of: Some(vec![
307 json_abi_type.into(),
308 borsh_abi_type.into(), ]),
310 ..Default::default()
311 })),
312 ..Default::default()
313 };
314 schema_object.metadata().description =
315 Some("Information about a single type (e.g. return type).".to_string());
316 schema_object.into()
317 }
318}
319
320#[derive(Serialize, Deserialize)]
321#[serde(remote = "BorshSchemaContainer")]
322struct BorshSchemaContainerDef {
323 #[serde(getter = "borsh_serde::getters::declaration")]
324 declaration: Declaration,
325 #[serde(with = "borsh_serde", getter = "borsh_serde::getters::definitions")]
326 definitions: BTreeMap<Declaration, Definition>,
327}
328
329impl From<BorshSchemaContainerDef> for BorshSchemaContainer {
330 fn from(value: BorshSchemaContainerDef) -> Self {
331 Self::new(value.declaration, value.definitions)
332 }
333}
334
335mod borsh_serde {
339 use super::*;
340 use serde::ser::SerializeMap;
341 use serde::{Deserializer, Serializer};
342 pub mod getters {
343 use super::*;
344
345 pub fn declaration(obj: &BorshSchemaContainer) -> &Declaration {
346 obj.declaration()
347 }
348
349 pub fn definitions(obj: &BorshSchemaContainer) -> BTreeMap<Declaration, Definition> {
350 let definitions: BTreeMap<Declaration, Definition> = obj
351 .definitions()
352 .map(|(k, v)| (k.clone(), v.clone()))
353 .collect();
354 definitions
355 }
356 }
357
358 #[derive(Serialize, Deserialize)]
359 #[serde(remote = "Definition")]
360 enum DefinitionDef {
361 Primitive(u8),
362 Sequence {
363 length_width: u8,
364 length_range: core::ops::RangeInclusive<u64>,
365 elements: Declaration,
366 },
367 #[serde(with = "transparent")]
368 Tuple {
369 elements: Vec<Declaration>,
370 },
371 Enum {
372 tag_width: u8,
373 variants: Vec<(DiscriminantValue, VariantName, Declaration)>,
374 },
375 #[serde(with = "transparent_fields")]
376 Struct {
377 fields: Fields,
378 },
379 }
380
381 #[derive(Serialize, Deserialize)]
382 struct HelperDefinition(#[serde(with = "DefinitionDef")] Definition);
383
384 mod transparent {
387 use serde::{Deserialize, Deserializer, Serialize, Serializer};
388
389 pub fn serialize<T, S>(field: &T, serializer: S) -> Result<S::Ok, S::Error>
390 where
391 T: Serialize,
392 S: Serializer,
393 {
394 serializer.serialize_some(&field)
395 }
396
397 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
398 where
399 T: Deserialize<'de>,
400 D: Deserializer<'de>,
401 {
402 T::deserialize(deserializer)
403 }
404 }
405
406 mod transparent_fields {
410 use borsh::schema::{Declaration, FieldName, Fields};
411 use serde::{Deserialize, Deserializer, Serialize, Serializer};
412
413 #[derive(Serialize, Deserialize)]
414 #[serde(remote = "Fields", untagged)]
415 enum FieldsDef {
416 NamedFields(Vec<(FieldName, Declaration)>),
417 UnnamedFields(Vec<Declaration>),
418 Empty,
419 }
420
421 #[derive(Serialize, Deserialize)]
422 struct HelperFields(#[serde(with = "FieldsDef")] Fields);
423
424 pub fn serialize<S>(fields: &Fields, serializer: S) -> Result<S::Ok, S::Error>
425 where
426 S: Serializer,
427 {
428 HelperFields(fields.clone()).serialize(serializer)
429 }
430
431 pub fn deserialize<'de, D>(deserializer: D) -> Result<Fields, D::Error>
432 where
433 D: Deserializer<'de>,
434 {
435 Ok(HelperFields::deserialize(deserializer)?.0)
436 }
437 }
438
439 pub fn serialize<S>(
440 map: &BTreeMap<Declaration, Definition>,
441 serializer: S,
442 ) -> Result<S::Ok, S::Error>
443 where
444 S: Serializer,
445 {
446 let mut map_ser = serializer.serialize_map(Some(map.len()))?;
447 for (k, v) in map {
448 map_ser.serialize_entry(k, &HelperDefinition(v.clone()))?;
449 }
450 map_ser.end()
451 }
452
453 pub fn deserialize<'de, D>(
454 deserializer: D,
455 ) -> Result<BTreeMap<Declaration, Definition>, D::Error>
456 where
457 D: Deserializer<'de>,
458 {
459 let map = BTreeMap::<Declaration, HelperDefinition>::deserialize(deserializer)?;
460 Ok(map
461 .into_iter()
462 .map(|(k, HelperDefinition(v))| (k, v))
463 .collect())
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470 use borsh::BorshSchema;
471
472 fn get_definitions(type_schema: &BorshSchemaContainer) -> BTreeMap<Declaration, Definition> {
473 let definitions: BTreeMap<Declaration, Definition> = type_schema
474 .definitions()
475 .map(|(k, v)| (k.clone(), v.clone()))
476 .collect();
477 definitions
478 }
479
480 #[test]
481 fn test_serde_abitype_borsh_array() {
482 let abi_type = AbiType::Borsh {
483 type_schema: borsh::schema_container_of::<[u32; 2]>(),
484 };
485 let expected_json_str = serde_json::to_string_pretty(&abi_type).unwrap();
486 insta::assert_snapshot!(expected_json_str);
487
488 if let AbiType::Borsh { type_schema } = serde_json::from_str(&expected_json_str).unwrap() {
489 assert_eq!(type_schema.declaration(), "[u32; 2]");
490 let definitions = get_definitions(&type_schema);
491 assert_eq!(definitions.len(), 2);
492 assert_eq!(
493 definitions.get("[u32; 2]").unwrap(),
494 &Definition::Sequence {
495 length_width: 0,
496 length_range: 2..=2,
497 elements: "u32".to_string()
498 }
499 );
500
501 assert_eq!(definitions.get("u32").unwrap(), &Definition::Primitive(4));
502 } else {
503 panic!("Unexpected serialization type")
504 }
505 }
506
507 #[test]
508 fn test_serde_abitype_borsh_sequence() {
509 let abi_type = AbiType::Borsh {
510 type_schema: borsh::schema_container_of::<Vec<u32>>(),
511 };
512 let expected_json_str = serde_json::to_string_pretty(&abi_type).unwrap();
513 insta::assert_snapshot!(expected_json_str);
514
515 if let AbiType::Borsh { type_schema } = serde_json::from_str(&expected_json_str).unwrap() {
516 assert_eq!(type_schema.declaration(), "Vec<u32>");
517 let definitions = get_definitions(&type_schema);
518 assert_eq!(definitions.len(), 2);
519 assert_eq!(
520 definitions.get("Vec<u32>").unwrap(),
521 &Definition::Sequence {
522 length_width: Definition::DEFAULT_LENGTH_WIDTH,
523 length_range: Definition::DEFAULT_LENGTH_RANGE,
524 elements: "u32".to_string()
525 }
526 );
527 assert_eq!(definitions.get("u32").unwrap(), &Definition::Primitive(4));
528 } else {
529 panic!("Unexpected serialization type")
530 }
531 }
532
533 #[test]
534 fn test_serde_abitype_borsh_tuple() {
535 let abi_type = AbiType::Borsh {
536 type_schema: borsh::schema_container_of::<(u32, u32)>(),
537 };
538 let expected_json_str = serde_json::to_string_pretty(&abi_type).unwrap();
539 insta::assert_snapshot!(expected_json_str);
540
541 if let AbiType::Borsh { type_schema } = serde_json::from_str(&expected_json_str).unwrap() {
542 assert_eq!(type_schema.declaration(), "(u32, u32)");
543 let definitions = get_definitions(&type_schema);
544 assert_eq!(definitions.len(), 2);
545 assert_eq!(
546 definitions.get("(u32, u32)").unwrap(),
547 &Definition::Tuple {
548 elements: vec!["u32".to_string(), "u32".to_string()]
549 }
550 );
551 assert_eq!(definitions.get("u32").unwrap(), &Definition::Primitive(4));
552 } else {
553 panic!("Unexpected serialization type")
554 }
555 }
556
557 #[test]
558 fn test_serde_abitype_borsh_enum() {
559 #[derive(BorshSchema)]
560 enum Either {
561 _Left(u32),
562 _Right(u32),
563 }
564 let abi_type = AbiType::Borsh {
565 type_schema: borsh::schema_container_of::<Either>(),
566 };
567 let expected_json_str = serde_json::to_string_pretty(&abi_type).unwrap();
568 insta::assert_snapshot!(expected_json_str);
569
570 if let AbiType::Borsh { type_schema } = serde_json::from_str(&expected_json_str).unwrap() {
571 assert_eq!(type_schema.declaration(), "Either");
572 let definitions = get_definitions(&type_schema);
573 assert_eq!(definitions.len(), 4);
574 assert_eq!(
575 definitions.get("Either").unwrap(),
576 &Definition::Enum {
577 tag_width: 1,
578 variants: vec![
579 (0, "_Left".to_string(), "Either_Left".to_string()),
580 (1, "_Right".to_string(), "Either_Right".to_string())
581 ]
582 }
583 );
584 } else {
585 panic!("Unexpected serialization type")
586 }
587 }
588
589 #[test]
590 fn test_serde_abitype_borsh_struct_named() {
591 #[derive(BorshSchema)]
592 struct Pair {
593 _first: u32,
594 _second: u32,
595 }
596 let abi_type = AbiType::Borsh {
597 type_schema: borsh::schema_container_of::<Pair>(),
598 };
599 let expected_json_str = serde_json::to_string_pretty(&abi_type).unwrap();
600 insta::assert_snapshot!(expected_json_str);
601
602 if let AbiType::Borsh { type_schema } = serde_json::from_str(&expected_json_str).unwrap() {
603 assert_eq!(type_schema.declaration(), "Pair");
604 let definitions = get_definitions(&type_schema);
605 assert_eq!(definitions.len(), 2);
606 assert_eq!(
607 definitions.get("Pair").unwrap(),
608 &Definition::Struct {
609 fields: Fields::NamedFields(vec![
610 ("_first".to_string(), "u32".to_string()),
611 ("_second".to_string(), "u32".to_string())
612 ])
613 }
614 );
615 } else {
616 panic!("Unexpected serialization type")
617 }
618 }
619
620 #[test]
621 fn test_serde_abitype_borsh_struct_unnamed() {
622 #[derive(BorshSchema)]
623 struct Pair(u32, u32);
624 let abi_type = AbiType::Borsh {
625 type_schema: borsh::schema_container_of::<Pair>(),
626 };
627 let expected_json_str = serde_json::to_string_pretty(&abi_type).unwrap();
628 insta::assert_snapshot!(expected_json_str);
629
630 if let AbiType::Borsh { type_schema } = serde_json::from_str(&expected_json_str).unwrap() {
631 assert_eq!(type_schema.declaration(), "Pair");
632 let definitions = get_definitions(&type_schema);
633 assert_eq!(definitions.len(), 2);
634 assert_eq!(
635 definitions.get("Pair").unwrap(),
636 &Definition::Struct {
637 fields: Fields::UnnamedFields(vec!["u32".to_string(), "u32".to_string()])
638 }
639 );
640 } else {
641 panic!("Unexpected serialization type")
642 }
643 }
644
645 #[test]
646 fn test_serde_abitype_borsh_struct_empty() {
647 #[derive(BorshSchema)]
648 struct Unit;
649 let abi_type = AbiType::Borsh {
650 type_schema: borsh::schema_container_of::<Unit>(),
651 };
652 let expected_json_str = serde_json::to_string_pretty(&abi_type).unwrap();
653 insta::assert_snapshot!(expected_json_str);
654
655 if let AbiType::Borsh { type_schema } = serde_json::from_str(&expected_json_str).unwrap() {
656 assert_eq!(type_schema.declaration(), "Unit");
657 let definitions = get_definitions(&type_schema);
658 assert_eq!(definitions.len(), 1);
659 assert_eq!(
660 definitions.get("Unit").unwrap(),
661 &Definition::Struct {
662 fields: Fields::Empty
663 }
664 );
665 } else {
666 panic!("Unexpected serialization type")
667 }
668 }
669
670 #[test]
671 fn test_de_error_abitype_unknown_fields() {
672 let json = r#"
673 {
674 "serialization_type": "borsh",
675 "extra": "blah-blah",
676 "type_schema": {
677 "declaration": "Unit",
678 "definitions": {
679 "Unit": {
680 "Struct": null
681 }
682 }
683 }
684 }
685 "#;
686 serde_json::from_str::<AbiType>(json)
687 .expect_err("Expected deserialization to fail due to unknown field");
688 }
689
690 #[test]
691 fn test_serde_abiborshparameter_struct_empty() {
692 #[derive(BorshSchema)]
693 struct Unit;
694 let expected_param = AbiBorshParameter {
695 name: "foo".to_string(),
696 type_schema: borsh::schema_container_of::<Unit>(),
697 };
698
699 let expected_json_str = serde_json::to_string_pretty(&expected_param).unwrap();
700 insta::assert_snapshot!(expected_json_str);
701
702 let param = serde_json::from_str::<AbiBorshParameter>(&expected_json_str).unwrap();
703 assert_eq!(param.name, "foo");
704 assert_eq!(param.type_schema.declaration(), "Unit");
705 let definitions = get_definitions(¶m.type_schema);
706 assert_eq!(definitions.len(), 1);
707 assert_eq!(
708 definitions.get("Unit").unwrap(),
709 &Definition::Struct {
710 fields: Fields::Empty
711 }
712 );
713 }
714
715 #[test]
716 fn test_de_error_abiborshparameter_unknown_fields() {
717 let json = r#"
718 {
719 "name": "foo",
720 "extra": "blah-blah",
721 "type_schema": {
722 "declaration": "Unit",
723 "definitions": {
724 "Unit": {
725 "Struct": null
726 }
727 }
728 }
729 }
730 "#;
731 serde_json::from_str::<AbiBorshParameter>(json)
732 .expect_err("Expected deserialization to fail due to unknown field");
733 }
734
735 #[test]
736 fn test_de_abiroot_correct_version() {
737 let json = format!(
738 r#"
739 {{
740 "schema_version": "{}",
741 "metadata": {{}},
742 "body": {{
743 "functions": [],
744 "root_schema": {{}}
745 }}
746 }}
747 "#,
748 SCHEMA_VERSION
749 );
750 let abi_root = serde_json::from_str::<AbiRoot>(&json).unwrap();
751 assert_eq!(abi_root.schema_version, SCHEMA_VERSION);
752 }
753
754 #[test]
755 fn test_de_error_abiroot_older_version() {
756 let json = r#"
757 {
758 "schema_version": "0.0.1",
759 "metadata": {},
760 "body": {
761 "functions": [],
762 "root_schema": {}
763 }
764 }
765 "#;
766 let err = serde_json::from_str::<AbiRoot>(json)
767 .expect_err("Expected deserialization to fail due to schema version mismatch");
768 assert!(err.to_string().contains(
769 "got 0.0.1: consider re-generating your ABI file with a newer version of SDK and cargo-unc"
770 ));
771 }
772
773 #[test]
774 fn test_de_error_abiroot_newer_version() {
775 let json = r#"
776 {
777 "schema_version": "99.99.99",
778 "metadata": {},
779 "body": {
780 "functions": [],
781 "root_schema": {}
782 }
783 }
784 "#;
785 let err = serde_json::from_str::<AbiRoot>(json)
786 .expect_err("Expected deserialization to fail due to schema version mismatch");
787 assert!(err
788 .to_string()
789 .contains("got 99.99.99: consider upgrading unc-abi to a newer version"));
790 }
791}