use anyhow::Result;
use serde::{Deserialize, Serialize};
use crate::std::Primitive;
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct StdLibFnData {
pub name: String,
pub summary: String,
pub description: String,
pub tags: Vec<String>,
pub args: Vec<StdLibFnArg>,
pub return_value: StdLibFnArg,
pub unpublished: bool,
pub deprecated: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct StdLibFnArg {
pub name: String,
pub type_: String,
pub schema: schemars::schema::Schema,
pub required: bool,
}
impl StdLibFnArg {
#[allow(dead_code)]
pub fn get_type_string(&self) -> Result<(String, bool)> {
get_type_string_from_schema(&self.schema)
}
#[allow(dead_code)]
pub fn description(&self) -> Option<String> {
get_description_string_from_schema(&self.schema)
}
}
pub trait StdLibFn {
fn name(&self) -> String;
fn summary(&self) -> String;
fn description(&self) -> String;
fn tags(&self) -> Vec<String>;
fn args(&self) -> Vec<StdLibFnArg>;
fn return_value(&self) -> StdLibFnArg;
fn unpublished(&self) -> bool;
fn deprecated(&self) -> bool;
fn std_lib_fn(&self) -> crate::std::StdFn;
fn to_json(&self) -> Result<StdLibFnData> {
Ok(StdLibFnData {
name: self.name(),
summary: self.summary(),
description: self.description(),
tags: self.tags(),
args: self.args(),
return_value: self.return_value(),
unpublished: self.unpublished(),
deprecated: self.deprecated(),
})
}
fn fn_signature(&self) -> String {
let mut signature = String::new();
signature.push_str(&format!("{}(", self.name()));
for (i, arg) in self.args().iter().enumerate() {
if i > 0 {
signature.push_str(", ");
}
signature.push_str(&format!("{}: {}", arg.name, arg.type_));
}
signature.push_str(") -> ");
signature.push_str(&self.return_value().type_);
signature
}
}
pub fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Option<String> {
if let schemars::schema::Schema::Object(o) = schema {
if let Some(metadata) = &o.metadata {
if let Some(description) = &metadata.description {
return Some(description.to_string());
}
}
}
None
}
pub fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(String, bool)> {
match schema {
schemars::schema::Schema::Object(o) => {
if let Some(format) = &o.format {
if format == "uuid" {
return Ok((Primitive::Uuid.to_string(), false));
} else if format == "double" || format == "uint" {
return Ok((Primitive::Number.to_string(), false));
} else {
anyhow::bail!("unknown format: {}", format);
}
}
if let Some(obj_val) = &o.object {
let mut fn_docs = String::new();
fn_docs.push_str("{\n");
for (prop_name, prop) in obj_val.properties.iter() {
if prop_name.starts_with('_') {
continue;
}
if let Some(description) = get_description_string_from_schema(prop) {
fn_docs.push_str(&format!("\t// {}\n", description));
}
fn_docs.push_str(&format!(
"\t\"{}\": {},\n",
prop_name,
get_type_string_from_schema(prop)?.0,
));
}
fn_docs.push('}');
return Ok((fn_docs, true));
}
if let Some(array_val) = &o.array {
if let Some(schemars::schema::SingleOrVec::Single(items)) = &array_val.items {
return Ok((format!("[{}]", get_type_string_from_schema(items)?.0), false));
} else if let Some(items) = &array_val.contains {
return Ok((format!("[{}]", get_type_string_from_schema(items)?.0), false));
}
}
if let Some(subschemas) = &o.subschemas {
let mut fn_docs = String::new();
if let Some(items) = &subschemas.one_of {
for (i, item) in items.iter().enumerate() {
fn_docs.push_str(&get_type_string_from_schema(item)?.0.to_string());
if i < items.len() - 1 {
fn_docs.push_str(" |\n");
}
}
} else if let Some(items) = &subschemas.any_of {
for (i, item) in items.iter().enumerate() {
fn_docs.push_str(&get_type_string_from_schema(item)?.0.to_string());
if i < items.len() - 1 {
fn_docs.push_str(" |\n");
}
}
} else {
anyhow::bail!("unknown subschemas: {:#?}", subschemas);
}
return Ok((fn_docs, true));
}
if let Some(schemars::schema::SingleOrVec::Single(_string)) = &o.instance_type {
return Ok((Primitive::String.to_string(), false));
}
anyhow::bail!("unknown type: {:#?}", o)
}
schemars::schema::Schema::Bool(_) => Ok((Primitive::Bool.to_string(), false)),
}
}