use std::path::Path;
use abstract_std::app::{self, AppExecuteMsg, AppQueryMsg};
use cosmwasm_schema::{export_schema_with_title, schema_for, write_api, QueryResponses};
use schemars::JsonSchema;
use serde::{de::DeserializeOwned, Serialize};
use crate::{
AppContract, AppError, ExecuteEndpoint, InstantiateEndpoint, MigrateEndpoint, QueryEndpoint,
};
impl<
Error: From<cosmwasm_std::StdError>
+ From<AppError>
+ From<abstract_sdk::AbstractSdkError>
+ From<abstract_std::AbstractError>
+ 'static,
CustomExecMsg: Serialize + JsonSchema + AppExecuteMsg,
CustomInitMsg: Serialize + DeserializeOwned + JsonSchema,
CustomQueryMsg: Serialize + JsonSchema + AppQueryMsg + QueryResponses,
CustomMigrateMsg: Serialize + JsonSchema,
SudoMsg: Serialize + JsonSchema,
> AppContract<Error, CustomInitMsg, CustomExecMsg, CustomQueryMsg, CustomMigrateMsg, SudoMsg>
{
pub fn export_schema(out_dir: &Path) {
write_api! {
name: "schema",
instantiate: app::InstantiateMsg<CustomInitMsg>,
query: app::QueryMsg<CustomQueryMsg>,
execute: app::ExecuteMsg<CustomExecMsg>,
migrate: app::MigrateMsg<CustomMigrateMsg>,
};
write_api! {
name: "module-schema",
instantiate: CustomInitMsg,
query: CustomQueryMsg,
execute: CustomExecMsg,
migrate: CustomMigrateMsg,
};
export_schema_with_title(
&schema_for!(<Self as ExecuteEndpoint>::ExecuteMsg),
out_dir,
"ExecuteMsg",
);
export_schema_with_title(
&schema_for!(<Self as InstantiateEndpoint>::InstantiateMsg),
out_dir,
"InstantiateMsg",
);
export_schema_with_title(
&schema_for!(<Self as QueryEndpoint>::QueryMsg),
out_dir,
"QueryMsg",
);
export_schema_with_title(
&schema_for!(<Self as MigrateEndpoint>::MigrateMsg),
out_dir,
"MigrateMsg",
);
}
pub fn export_schema_custom<CustomExecuteHandle: JsonSchema>(out_dir: &Path) {
let custom_execute_handle = schema_for!(CustomExecuteHandle);
let base = schema_for!(app::BaseExecuteMsg);
assert_have_endpoint(&custom_execute_handle, &base, "Base");
let module = schema_for!(CustomExecMsg);
assert_have_endpoint(&custom_execute_handle, &module, "Module");
write_api! {
name: "schema",
instantiate: app::InstantiateMsg<CustomInitMsg>,
query: app::QueryMsg<CustomQueryMsg>,
execute: CustomExecuteHandle,
migrate: app::MigrateMsg<CustomMigrateMsg>,
};
write_api! {
name: "module-schema",
instantiate: CustomInitMsg,
query: CustomQueryMsg,
execute: CustomExecMsg,
migrate: CustomMigrateMsg,
};
export_schema_with_title(&custom_execute_handle, out_dir, "ExecuteMsg");
export_schema_with_title(
&schema_for!(<Self as InstantiateEndpoint>::InstantiateMsg),
out_dir,
"InstantiateMsg",
);
export_schema_with_title(
&schema_for!(<Self as QueryEndpoint>::QueryMsg),
out_dir,
"QueryMsg",
);
export_schema_with_title(
&schema_for!(<Self as MigrateEndpoint>::MigrateMsg),
out_dir,
"MigrateMsg",
);
}
}
fn assert_have_endpoint(
custom: &schemars::schema::RootSchema,
expected: &schemars::schema::RootSchema,
variant_name: &str,
) {
let variants = custom.schema.subschemas.clone().unwrap().one_of.unwrap();
let expected_title = expected.schema.metadata.clone().unwrap().title.unwrap();
let have_base = variants.iter().any(|schema| {
if let schemars::schema::Schema::Object(schema) = schema {
if let Some(object) = &schema.object {
return object.required == schemars::Set::from([variant_name.to_lowercase()])
&& object
.properties
.get(&variant_name.to_lowercase())
.cloned()
.unwrap()
.into_object()
.reference
.unwrap()
== format!("#/definitions/{expected_title}",);
}
}
false
});
assert!(
have_base,
"Custom Execute variant must include {variant_name}({expected_title})"
);
let mut custom_definition = custom
.definitions
.get(&expected_title)
.cloned()
.unwrap()
.into_object();
custom_definition.metadata = expected.schema.metadata.clone();
assert_eq!(
custom_definition, expected.schema,
"Custom Execute variant {variant_name}({expected_title}) have invalid type"
);
}