1use aluvm::{Lib, LibId};
25use amplify::confinement::{SmallOrdMap, SmallOrdSet, TinyOrdMap};
26use commit_verify::ReservedBytes;
27use strict_encoding::{StrictDeserialize, StrictSerialize, TypeName};
28use strict_types::TypeSystem;
29use ultrasonic::{CallId, Codex, LibRepo};
30
31use crate::sigs::ContentSigs;
32use crate::{Annotations, Api, MergeError, MethodName, LIB_NAME_SONIC};
33
34#[derive(Clone, Eq, PartialEq, Debug)]
36#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
37#[strict_type(lib = LIB_NAME_SONIC)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
39pub struct Schema {
40 pub codex: Codex,
41 pub default_api: Api,
42 pub default_api_sigs: ContentSigs,
43 pub custom_apis: SmallOrdMap<Api, ContentSigs>,
44 pub libs: SmallOrdSet<Lib>,
45 pub types: TypeSystem,
46 pub codex_sigs: ContentSigs,
47 pub annotations: TinyOrdMap<Annotations, ContentSigs>,
48 pub reserved: ReservedBytes<8>,
49}
50
51impl StrictSerialize for Schema {}
52impl StrictDeserialize for Schema {}
53
54impl LibRepo for Schema {
55 fn get_lib(&self, lib_id: LibId) -> Option<&Lib> { self.libs.iter().find(|lib| lib.lib_id() == lib_id) }
56}
57
58impl Schema {
59 pub fn new(codex: Codex, api: Api, libs: impl IntoIterator<Item = Lib>, types: TypeSystem) -> Self {
60 Schema {
62 codex,
63 default_api: api,
64 default_api_sigs: none!(),
65 custom_apis: none!(),
66 libs: SmallOrdSet::from_iter_checked(libs),
67 types,
68 codex_sigs: none!(),
69 annotations: none!(),
70 reserved: zero!(),
71 }
72 }
73
74 pub fn api(&self, name: &TypeName) -> &Api {
75 self.custom_apis
76 .keys()
77 .find(|api| api.name() == Some(name))
78 .expect("API is not known")
79 }
80
81 pub fn call_id(&self, method: impl Into<MethodName>) -> CallId {
82 self.default_api
83 .verifier(method)
84 .expect("calling to method absent in Codex API")
85 }
86
87 pub fn merge(&mut self, other: Self) -> Result<bool, MergeError> {
88 if self.codex.codex_id() != other.codex.codex_id() {
89 return Err(MergeError::CodexMismatch);
90 }
91 self.codex_sigs.merge(other.codex_sigs);
92
93 if self.default_api != other.default_api {
94 let _ = self
95 .custom_apis
96 .insert(other.default_api, other.default_api_sigs);
97 } else {
98 self.default_api_sigs.merge(other.default_api_sigs);
99 }
100
101 for (api, other_sigs) in other.custom_apis {
102 let Ok(entry) = self.custom_apis.entry(api) else {
103 continue;
104 };
105 entry.or_default().merge(other_sigs);
106 }
107
108 let _ = self.libs.extend(other.libs);
112 let _ = self.types.extend(other.types);
113
114 for (annotation, other_sigs) in other.annotations {
115 let Ok(entry) = self.annotations.entry(annotation) else {
116 continue;
117 };
118 entry.or_default().merge(other_sigs);
119 }
120
121 Ok(true)
122 }
123}
124
125#[cfg(feature = "std")]
126mod _fs {
127 use std::path::Path;
128
129 use strict_encoding::{DeserializeError, SerializeError, StrictDeserialize, StrictSerialize};
130
131 use super::Schema;
132
133 impl Schema {
134 pub fn load(path: impl AsRef<Path>) -> Result<Self, DeserializeError> {
135 Self::strict_deserialize_from_file::<{ usize::MAX }>(path)
136 }
137
138 pub fn save(&self, path: impl AsRef<Path>) -> Result<(), SerializeError> {
139 self.strict_serialize_to_file::<{ usize::MAX }>(path)
140 }
141 }
142}