1use std::collections::BTreeMap;
4
5use schemars::schema::RootSchema;
6use thiserror::Error;
7
8pub const IDL_VERSION: &str = "1.0.0";
13
14pub struct Api {
16 pub contract_name: String,
17 pub contract_version: String,
18 pub instantiate: Option<RootSchema>,
19 pub execute: Option<RootSchema>,
20 pub query: Option<RootSchema>,
21 pub migrate: Option<RootSchema>,
22 pub sudo: Option<RootSchema>,
23 pub responses: Option<BTreeMap<String, RootSchema>>,
25}
26
27impl Api {
28 pub fn render(self) -> JsonApi {
29 let mut json_api = JsonApi {
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 if let Some(instantiate) = &mut json_api.instantiate {
42 if let Some(metadata) = &mut instantiate.schema.metadata {
43 metadata.title = Some("InstantiateMsg".to_string());
44 }
45 }
46 if let Some(execute) = &mut json_api.execute {
47 if let Some(metadata) = &mut execute.schema.metadata {
48 metadata.title = Some("ExecuteMsg".to_string());
49 }
50 }
51 if let Some(query) = &mut json_api.query {
52 if let Some(metadata) = &mut query.schema.metadata {
53 metadata.title = Some("QueryMsg".to_string());
54 }
55 }
56 if let Some(migrate) = &mut json_api.migrate {
57 if let Some(metadata) = &mut migrate.schema.metadata {
58 metadata.title = Some("MigrateMsg".to_string());
59 }
60 }
61 if let Some(sudo) = &mut json_api.sudo {
62 if let Some(metadata) = &mut sudo.schema.metadata {
63 metadata.title = Some("SudoMsg".to_string());
64 }
65 }
66
67 json_api
68 }
69}
70
71#[derive(serde::Serialize)]
73pub struct JsonApi {
74 contract_name: String,
75 contract_version: String,
76 idl_version: String,
77 instantiate: Option<RootSchema>,
78 execute: Option<RootSchema>,
79 query: Option<RootSchema>,
80 migrate: Option<RootSchema>,
81 sudo: Option<RootSchema>,
82 responses: Option<BTreeMap<String, RootSchema>>,
83}
84
85impl JsonApi {
86 pub fn to_string(&self) -> Result<String, EncodeError> {
87 serde_json::to_string_pretty(&self).map_err(Into::into)
88 }
89
90 pub fn to_schema_files(&self) -> Result<Vec<(String, String)>, EncodeError> {
91 let mut result = Vec::new();
92
93 if let Some(instantiate) = &self.instantiate {
94 result.push((
95 "instantiate.json".to_string(),
96 serde_json::to_string_pretty(&instantiate)?,
97 ));
98 }
99
100 if let Some(execute) = &self.execute {
101 result.push((
102 "execute.json".to_string(),
103 serde_json::to_string_pretty(&execute)?,
104 ));
105 }
106 if let Some(query) = &self.query {
107 result.push((
108 "query.json".to_string(),
109 serde_json::to_string_pretty(&query)?,
110 ));
111 }
112 if let Some(migrate) = &self.migrate {
113 result.push((
114 "migrate.json".to_string(),
115 serde_json::to_string_pretty(&migrate)?,
116 ));
117 }
118 if let Some(sudo) = &self.sudo {
119 result.push((
120 "sudo.json".to_string(),
121 serde_json::to_string_pretty(&sudo)?,
122 ));
123 }
124 if let Some(responses) = &self.responses {
125 for (name, response) in responses {
126 result.push((
127 format!("response_to_{name}.json"),
128 serde_json::to_string_pretty(&response)?,
129 ));
130 }
131 }
132
133 Ok(result)
134 }
135
136 pub fn to_writer(&self, writer: impl std::io::Write) -> Result<(), EncodeError> {
137 serde_json::to_writer_pretty(writer, self).map_err(Into::into)
138 }
139}
140
141#[derive(Error, Debug)]
142pub enum EncodeError {
143 #[error("{0}")]
144 JsonError(#[from] serde_json::Error),
145}
146
147#[cfg(test)]
148mod tests {
149 use crate::schema_for;
150
151 use super::*;
152
153 #[test]
154 fn version_is_semver() {
155 semver::Version::parse(IDL_VERSION).unwrap();
156 }
157
158 #[test]
159 fn to_schema_files_works() {
160 let empty = Api {
161 contract_name: "my_contract".to_string(),
162 contract_version: "1.2.3".to_string(),
163 instantiate: None,
164 execute: None,
165 query: None,
166 migrate: None,
167 sudo: None,
168 responses: None,
169 };
170
171 let files = empty.render().to_schema_files().unwrap();
172 assert_eq!(files, []);
173
174 #[derive(schemars::JsonSchema)]
175 struct TestMsg {}
176
177 let full = Api {
178 contract_name: "my_contract".to_string(),
179 contract_version: "1.2.3".to_string(),
180 instantiate: Some(schema_for!(TestMsg)),
181 execute: Some(schema_for!(TestMsg)),
182 query: Some(schema_for!(TestMsg)),
183 migrate: Some(schema_for!(TestMsg)),
184 sudo: Some(schema_for!(TestMsg)),
185 responses: Some(BTreeMap::from([(
186 "TestMsg".to_string(),
187 schema_for!(TestMsg),
188 )])),
189 };
190
191 let files = full.render().to_schema_files().unwrap();
192 assert_eq!(files.len(), 6);
193 assert_eq!(files[0].0, "instantiate.json");
194 assert_eq!(files[1].0, "execute.json");
195 assert_eq!(files[2].0, "query.json");
196 assert_eq!(files[3].0, "migrate.json");
197 assert_eq!(files[4].0, "sudo.json");
198 assert_eq!(files[5].0, "response_to_TestMsg.json");
199 }
200}