1use std::collections::BTreeMap;
4
5use schemars::schema::RootSchema;
6use thiserror::Error;
7
8pub const IDL_VERSION: &str = "1.0.0";
13
14pub struct CwApi {
16 pub contract_name: String,
17 pub contract_version: String,
18 pub instantiate: Option<cw_schema::Schema>,
19 pub execute: Option<cw_schema::Schema>,
20 pub query: Option<cw_schema::Schema>,
21 pub migrate: Option<cw_schema::Schema>,
22 pub sudo: Option<cw_schema::Schema>,
23 pub responses: Option<BTreeMap<String, cw_schema::Schema>>,
25}
26
27impl CwApi {
28 pub fn render(self) -> JsonCwApi {
29 JsonCwApi {
30 contract_name: self.contract_name,
31 contract_version: self.contract_version,
32 idl_version: IDL_VERSION.to_string(),
33 instantiate: self.instantiate,
34 execute: self.execute,
35 query: self.query,
36 migrate: self.migrate,
37 sudo: self.sudo,
38 responses: self.responses,
39 }
40 }
41}
42
43pub struct Api {
45 pub contract_name: String,
46 pub contract_version: String,
47 pub instantiate: Option<RootSchema>,
48 pub execute: Option<RootSchema>,
49 pub query: Option<RootSchema>,
50 pub migrate: Option<RootSchema>,
51 pub sudo: Option<RootSchema>,
52 pub responses: Option<BTreeMap<String, RootSchema>>,
54}
55
56impl Api {
57 pub fn render(self) -> JsonApi {
58 let mut json_api = JsonApi {
59 contract_name: self.contract_name,
60 contract_version: self.contract_version,
61 idl_version: IDL_VERSION.to_string(),
62 instantiate: self.instantiate,
63 execute: self.execute,
64 query: self.query,
65 migrate: self.migrate,
66 sudo: self.sudo,
67 responses: self.responses,
68 };
69
70 if let Some(instantiate) = &mut json_api.instantiate {
71 if let Some(metadata) = &mut instantiate.schema.metadata {
72 metadata.title = Some("InstantiateMsg".to_string());
73 }
74 }
75 if let Some(execute) = &mut json_api.execute {
76 if let Some(metadata) = &mut execute.schema.metadata {
77 metadata.title = Some("ExecuteMsg".to_string());
78 }
79 }
80 if let Some(query) = &mut json_api.query {
81 if let Some(metadata) = &mut query.schema.metadata {
82 metadata.title = Some("QueryMsg".to_string());
83 }
84 }
85 if let Some(migrate) = &mut json_api.migrate {
86 if let Some(metadata) = &mut migrate.schema.metadata {
87 metadata.title = Some("MigrateMsg".to_string());
88 }
89 }
90 if let Some(sudo) = &mut json_api.sudo {
91 if let Some(metadata) = &mut sudo.schema.metadata {
92 metadata.title = Some("SudoMsg".to_string());
93 }
94 }
95
96 json_api
97 }
98}
99
100#[derive(serde::Deserialize, serde::Serialize)]
102pub struct JsonCwApi {
103 pub contract_name: String,
104 pub contract_version: String,
105 pub idl_version: String,
106 pub instantiate: Option<cw_schema::Schema>,
107 pub execute: Option<cw_schema::Schema>,
108 pub query: Option<cw_schema::Schema>,
109 pub migrate: Option<cw_schema::Schema>,
110 pub sudo: Option<cw_schema::Schema>,
111 pub responses: Option<BTreeMap<String, cw_schema::Schema>>,
112}
113
114impl JsonCwApi {
115 pub fn to_string(&self) -> Result<String, EncodeError> {
116 serde_json::to_string_pretty(&self).map_err(Into::into)
117 }
118
119 pub fn to_schema_files(&self) -> Result<Vec<(String, String)>, EncodeError> {
120 let mut result = Vec::new();
121
122 if let Some(instantiate) = &self.instantiate {
123 result.push((
124 "instantiate.json".to_string(),
125 serde_json::to_string_pretty(&instantiate)?,
126 ));
127 }
128
129 if let Some(execute) = &self.execute {
130 result.push((
131 "execute.json".to_string(),
132 serde_json::to_string_pretty(&execute)?,
133 ));
134 }
135 if let Some(query) = &self.query {
136 result.push((
137 "query.json".to_string(),
138 serde_json::to_string_pretty(&query)?,
139 ));
140 }
141 if let Some(migrate) = &self.migrate {
142 result.push((
143 "migrate.json".to_string(),
144 serde_json::to_string_pretty(&migrate)?,
145 ));
146 }
147 if let Some(sudo) = &self.sudo {
148 result.push((
149 "sudo.json".to_string(),
150 serde_json::to_string_pretty(&sudo)?,
151 ));
152 }
153 if let Some(responses) = &self.responses {
154 for (name, response) in responses {
155 result.push((
156 format!("response_to_{name}.json"),
157 serde_json::to_string_pretty(&response)?,
158 ));
159 }
160 }
161
162 Ok(result)
163 }
164
165 pub fn to_writer(&self, writer: impl std::io::Write) -> Result<(), EncodeError> {
166 serde_json::to_writer_pretty(writer, self).map_err(Into::into)
167 }
168}
169
170#[derive(serde::Serialize)]
172pub struct JsonApi {
173 contract_name: String,
174 contract_version: String,
175 idl_version: String,
176 instantiate: Option<RootSchema>,
177 execute: Option<RootSchema>,
178 query: Option<RootSchema>,
179 migrate: Option<RootSchema>,
180 sudo: Option<RootSchema>,
181 responses: Option<BTreeMap<String, RootSchema>>,
182}
183
184impl JsonApi {
185 pub fn to_string(&self) -> Result<String, EncodeError> {
186 serde_json::to_string_pretty(&self).map_err(Into::into)
187 }
188
189 pub fn to_schema_files(&self) -> Result<Vec<(String, String)>, EncodeError> {
190 let mut result = Vec::new();
191
192 if let Some(instantiate) = &self.instantiate {
193 result.push((
194 "instantiate.json".to_string(),
195 serde_json::to_string_pretty(&instantiate)?,
196 ));
197 }
198
199 if let Some(execute) = &self.execute {
200 result.push((
201 "execute.json".to_string(),
202 serde_json::to_string_pretty(&execute)?,
203 ));
204 }
205 if let Some(query) = &self.query {
206 result.push((
207 "query.json".to_string(),
208 serde_json::to_string_pretty(&query)?,
209 ));
210 }
211 if let Some(migrate) = &self.migrate {
212 result.push((
213 "migrate.json".to_string(),
214 serde_json::to_string_pretty(&migrate)?,
215 ));
216 }
217 if let Some(sudo) = &self.sudo {
218 result.push((
219 "sudo.json".to_string(),
220 serde_json::to_string_pretty(&sudo)?,
221 ));
222 }
223 if let Some(responses) = &self.responses {
224 for (name, response) in responses {
225 result.push((
226 format!("response_to_{name}.json"),
227 serde_json::to_string_pretty(&response)?,
228 ));
229 }
230 }
231
232 Ok(result)
233 }
234
235 pub fn to_writer(&self, writer: impl std::io::Write) -> Result<(), EncodeError> {
236 serde_json::to_writer_pretty(writer, self).map_err(Into::into)
237 }
238}
239
240#[derive(Error, Debug)]
241pub enum EncodeError {
242 #[error("{0}")]
243 JsonError(#[from] serde_json::Error),
244}
245
246#[cfg(test)]
247mod tests {
248 use crate::schema_for;
249
250 use super::*;
251
252 #[test]
253 fn version_is_semver() {
254 semver::Version::parse(IDL_VERSION).unwrap();
255 }
256
257 #[test]
258 fn to_schema_files_works() {
259 let empty = Api {
260 contract_name: "my_contract".to_string(),
261 contract_version: "1.2.3".to_string(),
262 instantiate: None,
263 execute: None,
264 query: None,
265 migrate: None,
266 sudo: None,
267 responses: None,
268 };
269
270 let files = empty.render().to_schema_files().unwrap();
271 assert_eq!(files, []);
272
273 #[derive(schemars::JsonSchema)]
274 struct TestMsg {}
275
276 let full = Api {
277 contract_name: "my_contract".to_string(),
278 contract_version: "1.2.3".to_string(),
279 instantiate: Some(schema_for!(TestMsg)),
280 execute: Some(schema_for!(TestMsg)),
281 query: Some(schema_for!(TestMsg)),
282 migrate: Some(schema_for!(TestMsg)),
283 sudo: Some(schema_for!(TestMsg)),
284 responses: Some(BTreeMap::from([(
285 "TestMsg".to_string(),
286 schema_for!(TestMsg),
287 )])),
288 };
289
290 let files = full.render().to_schema_files().unwrap();
291 assert_eq!(files.len(), 6);
292 assert_eq!(files[0].0, "instantiate.json");
293 assert_eq!(files[1].0, "execute.json");
294 assert_eq!(files[2].0, "query.json");
295 assert_eq!(files[3].0, "migrate.json");
296 assert_eq!(files[4].0, "sudo.json");
297 assert_eq!(files[5].0, "response_to_TestMsg.json");
298 }
299}