use crate::error::{ZomeApiError, ZomeApiResult};
use holochain_core_types::{
agent::AgentId,
dna::entry_types::EntryTypeDef,
entry::{entry_type::EntryType, AppEntryValue, Entry},
validation::{EntryValidationData, LinkValidationData, ValidationPackageDefinition},
};
use holochain_wasm_utils::api_serialization::validation::LinkDirection;
use std::convert::TryFrom;
pub type PackageCreator = Box<dyn FnMut() -> ValidationPackageDefinition + Sync>;
pub type Validator = Box<dyn FnMut(EntryValidationData<Entry>) -> Result<(), String> + Sync>;
pub type AgentValidator = Box<dyn FnMut(EntryValidationData<AgentId>) -> Result<(), String> + Sync>;
pub type LinkValidator = Box<dyn FnMut(LinkValidationData) -> Result<(), String> + Sync>;
pub struct ValidatingEntryType {
pub name: EntryType,
pub entry_type_definition: EntryTypeDef,
pub package_creator: PackageCreator,
pub validator: Validator,
pub links: Vec<ValidatingLinkDefinition>,
}
pub struct ValidatingLinkDefinition {
pub direction: LinkDirection,
pub other_entry_type: String,
pub link_type: String,
pub package_creator: PackageCreator,
pub validator: LinkValidator,
}
#[macro_export]
macro_rules! entry {
(
name: $name:expr,
description: $properties:expr,
sharing: $sharing:expr,
// $(native_type: $native_type:ty,)*
validation_package: || $package_creator:expr,
validation: | $validation_data:ident : hdk::EntryValidationData<$native_type:ty> | $entry_validation:expr
$(
,
links : [
$( $link_expr:expr ),*
]
)*
) => (
{
let mut entry_type = $crate::holochain_core_types::dna::entry_types::EntryTypeDef::new();
entry_type.properties = $crate::holochain_json_api::json::JsonString::from($properties);
entry_type.sharing = $sharing;
$($(
match $link_expr.direction {
$crate::LinkDirection::To => {
entry_type.links_to.push(
$crate::holochain_core_types::dna::entry_types::LinksTo{
target_type: $link_expr.other_entry_type,
link_type: $link_expr.link_type,
}
);
},
$crate::LinkDirection::From => {
entry_type.linked_from.push(
$crate::holochain_core_types::dna::entry_types::LinkedFrom{
base_type: $link_expr.other_entry_type,
link_type: $link_expr.link_type,
}
);
}
}
)*)*
let package_creator = Box::new(|| {
$package_creator
});
let validator = Box::new(|validation_data: $crate::holochain_wasm_utils::holochain_core_types::validation::EntryValidationData<$crate::holochain_core_types::entry::Entry>| {
let $validation_data = $crate::entry_definition::entry_to_native_type::<$native_type>(validation_data.clone())?;
use std::convert::TryFrom;
let e_type = $crate::holochain_core_types::entry::entry_type::EntryType::try_from(validation_data)?;
match e_type {
$crate::holochain_core_types::entry::entry_type::EntryType::App(_) => {
$entry_validation
},
$crate::holochain_core_types::entry::entry_type::EntryType::Deletion =>
{
$entry_validation
}
_ => {
Err(String::from("Schema validation failed"))?
}
}
});
$crate::entry_definition::ValidatingEntryType {
name: $crate::holochain_core_types::entry::entry_type::EntryType::App($crate::holochain_core_types::entry::entry_type::AppEntryType::from($name.to_string())),
entry_type_definition: entry_type,
package_creator,
validator,
links: vec![
$($(
$link_expr
),*)*
],
}
}
);
}
#[macro_export]
macro_rules! link {
(
direction: $direction:expr,
other_type: $other_type:expr,
link_type: $link_type:expr,
validation_package: || $package_creator:expr,
validation: | $validation_data:ident : hdk::LinkValidationData | $link_validation:expr
) => (
{
let package_creator = Box::new(|| {
$package_creator
});
let validator = Box::new(|validation_data: $crate::holochain_wasm_utils::holochain_core_types::validation::LinkValidationData| {
let $validation_data = validation_data;
$link_validation
});
$crate::entry_definition::ValidatingLinkDefinition {
direction: $direction,
other_entry_type: String::from($other_type),
link_type: String::from($link_type),
package_creator,
validator,
}
}
);
}
#[macro_export]
macro_rules! to {
(
$other_type:expr,
link_type: $link_type:expr,
validation_package: || $package_creator:expr,
validation: | $validation_data:ident : hdk::LinkValidationData | $link_validation:expr
) => (
link!(
direction: $crate::LinkDirection::To,
other_type: $other_type,
link_type: $link_type,
validation_package: || $package_creator,
validation: | $validation_data : hdk::LinkValidationData | $link_validation
)
)
}
#[macro_export]
macro_rules! from {
(
$other_type:expr,
link_type: $link_type:expr,
validation_package: || $package_creator:expr,
validation: | $validation_data:ident : hdk::LinkValidationData | $link_validation:expr
) => (
link!(
direction: $crate::LinkDirection::From,
other_type: $other_type,
link_type: $link_type,
validation_package: || $package_creator,
validation: | $validation_data : hdk::LinkValidationData | $link_validation
)
)
}
pub fn entry_to_native_type<T: TryFrom<AppEntryValue> + Clone>(
entry_validation: EntryValidationData<Entry>,
) -> ZomeApiResult<EntryValidationData<T>> {
match entry_validation {
EntryValidationData::Create {
entry,
validation_data,
} => {
let native_type = convert_entry_validation_to_native::<T>(entry)?;
Ok(EntryValidationData::Create {
entry: native_type,
validation_data,
})
}
EntryValidationData::Modify {
new_entry,
old_entry,
old_entry_header,
validation_data,
} => {
let new_entry = convert_entry_validation_to_native::<T>(new_entry)?;
let old_entry = convert_entry_validation_to_native::<T>(old_entry)?;
Ok(EntryValidationData::Modify {
new_entry,
old_entry,
old_entry_header,
validation_data,
})
}
EntryValidationData::Delete {
old_entry,
old_entry_header,
validation_data,
} => {
let old_entry = convert_entry_validation_to_native::<T>(old_entry)?;
Ok(EntryValidationData::Delete {
old_entry,
old_entry_header,
validation_data,
})
}
}
}
fn convert_entry_validation_to_native<T: TryFrom<AppEntryValue> + Clone>(
entry: Entry,
) -> ZomeApiResult<T> {
match entry {
Entry::App(_, entry_value) => T::try_from(entry_value.to_owned()).map_err(|_| {
ZomeApiError::Internal(
vec![
"Could not convert Entry result to requested type : ".to_string(),
entry_value.to_string(),
]
.join(&String::new()),
)
}),
_ => Err(ZomeApiError::Internal(
"Entry did not return an app entry".to_string(),
)),
}
}