use std::collections::HashMap;
#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
pub enum WrappedCandidType {
Bool,
Nat,
Int,
Nat8,
Nat16,
Nat32,
Nat64,
Int8,
Int16,
Int32,
Int64,
Float32,
Float64,
Null,
Text,
Principal,
Vec(Box<WrappedCandidType>),
Opt(Box<WrappedCandidType>),
Record(Vec<(String, WrappedCandidType)>),
Variant(Vec<(String, Option<WrappedCandidType>)>),
Tuple(Vec<WrappedCandidType>),
Unknown,
Empty, Reserved, Func {
args: Vec<WrappedCandidType>,
results: Vec<WrappedCandidType>,
annotation: FunctionAnnotation,
},
Service {
args: Vec<WrappedCandidType>,
methods: Vec<(String, WrappedCandidType)>, },
Rec(Box<WrappedCandidType>, u32), Reference(u32), }
#[derive(CandidType, Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
pub enum FunctionAnnotation {
None, Query, Oneway, }
fn wrapped_key_word(name: &str) -> String {
if match name {
"bool" => true,
"nat" => true,
"int" => true,
"nat8" => true,
"nat16" => true,
"nat32" => true,
"nat64" => true,
"int8" => true,
"int16" => true,
"int32" => true,
"int64" => true,
"float32" => true,
"float64" => true,
"null" => true,
"text" => true,
"principal" => true,
"vec" => true,
"opt" => true,
"record" => true,
"variant" => true,
"unknown" => true,
"empty" => true,
"reserved" => true,
"func" => true,
"service" => true,
"rec" => true, _ => false,
} || name.contains(" ")
|| name.contains("\\")
{
format!("\"{}\"", name)
} else {
name.to_string()
}
}
impl WrappedCandidType {
pub fn to_text(&self) -> String {
match self {
Self::Bool => String::from("bool"),
Self::Nat => String::from("nat"),
Self::Int => String::from("int"),
Self::Nat8 => String::from("nat8"),
Self::Nat16 => String::from("nat16"),
Self::Nat32 => String::from("nat32"),
Self::Nat64 => String::from("nat64"),
Self::Int8 => String::from("int8"),
Self::Int16 => String::from("int16"),
Self::Int32 => String::from("int32"),
Self::Int64 => String::from("int64"),
Self::Float32 => String::from("float32"),
Self::Float64 => String::from("float64"),
Self::Null => String::from("null"),
Self::Text => String::from("text"),
Self::Principal => String::from("principal"),
Self::Vec(subtype) => format!("vec {}", subtype.to_text()),
Self::Opt(subtype) => format!("opt {}", subtype.to_text()),
Self::Record(subitems) => format!(
"record {{ {} }}",
subitems
.iter()
.map(|(name, subtype)| format!(
"{} : {}",
wrapped_key_word(name),
subtype.to_text()
))
.collect::<Vec<_>>()
.join("; ")
),
Self::Variant(subitems) => format!(
"variant {{ {} }}",
subitems
.iter()
.map(|(name, subtype)| {
if let Some(subtype) = subtype {
format!("{} : {}", wrapped_key_word(name), subtype.to_text())
} else {
format!("{}", wrapped_key_word(name),)
}
})
.collect::<Vec<_>>()
.join("; ")
),
Self::Tuple(subitems) => format!(
"variant {{ {} }}",
subitems
.iter()
.map(|subtype| format!("{}", subtype.to_text()))
.collect::<Vec<_>>()
.join("; ")
),
Self::Unknown => String::from("unknown"),
Self::Empty => String::from("empty"),
Self::Reserved => String::from("reserved"),
Self::Func {
args,
results,
annotation,
} => format!(
"func ({}) -> ({}){}",
args.iter()
.map(|t| t.to_text())
.collect::<Vec<_>>()
.join(", "),
results
.iter()
.map(|t| t.to_text())
.collect::<Vec<_>>()
.join(", "),
match annotation {
FunctionAnnotation::None => "",
FunctionAnnotation::Query => " query",
FunctionAnnotation::Oneway => " oneway",
}
),
Self::Service { args, methods } => format!(
"service :{} {{\n{}\n}}",
if args.is_empty() {
"".to_string()
} else {
format!(
" ({}) ->",
args.iter()
.map(|t| t.to_text())
.collect::<Vec<_>>()
.join(", ")
)
},
methods
.iter()
.map(|(name, func)| format!(
" {} : {};",
wrapped_key_word(name),
func.to_text()
))
.collect::<Vec<_>>()
.join("\n"),
),
Self::Rec(subtype, id) => format!("μrec_{}.{}", id, subtype.to_text()),
Self::Reference(id) => format!("rec_{}", id),
}
}
pub fn to_methods(&self) -> HashMap<String, String> {
match self {
Self::Service { methods, .. } => methods
.iter()
.map(|(method, candid)| {
(method.to_string(), {
let func = candid.to_text();
if func.starts_with("func ") {
func[5..].to_string()
} else {
func
}
})
})
.collect(),
_ => panic!("must be service"),
}
}
}