use super::util::{add_assert, add_assert_frags, new_kb_test};
use crate::codegen::template::basic::{
AppendedFragment, AtomicFragment, FileFragment, VecFragment,
};
use crate::codegen::StructConfig;
use indoc::formatdoc;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Clone)]
pub struct InternalNameConfig {
pub getter: &'static str,
pub setter: &'static str,
pub suffix: &'static str,
pub added_attributes: &'static str,
pub all_attributes: &'static str,
pub attr_import: Option<&'static str>,
}
impl InternalNameConfig {
pub const DEFAULT: Self = Self {
getter: "internal_name",
setter: "set_internal_name",
suffix: ".to_owned()",
added_attributes: "introduced_attribute_archetypes",
all_attributes: "attribute_archetypes",
attr_import: Some("zamm_yin::tao::form::FormTrait"),
};
pub const YIN_AT_LEAST_0_1_1: Self = Self {
getter: "internal_name_str",
setter: "set_internal_name_str",
suffix: "",
added_attributes: "introduced_attribute_archetypes",
all_attributes: "attribute_archetypes",
attr_import: Some("zamm_yin::tao::form::FormTrait"),
};
pub const YIN_AT_LEAST_0_1_4: Self = Self {
getter: "internal_name_str",
setter: "set_internal_name_str",
suffix: "",
added_attributes: "added_attributes",
all_attributes: "attributes",
attr_import: None,
};
}
impl Default for InternalNameConfig {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Clone)]
pub struct TaoConfig {
pub imports: Option<String>,
pub this: StructConfig,
pub internal_name: String,
pub internal_name_cfg: InternalNameConfig,
pub form: StructConfig,
pub parent_name: String,
pub parent_import: String,
pub archetype: StructConfig,
pub all_attributes: Vec<String>,
pub all_attribute_imports: Vec<String>,
pub introduced_attributes: Vec<String>,
pub introduced_attribute_imports: Vec<String>,
pub doc: String,
pub id: String,
}
impl Default for TaoConfig {
fn default() -> Self {
Self {
imports: Some("zamm_yin::tao::YIN_MAX_ID".to_owned()),
this: StructConfig::default(),
internal_name: "dummy".to_owned(),
internal_name_cfg: InternalNameConfig::DEFAULT,
form: StructConfig::default(),
parent_name: "Tao".to_owned(),
parent_import: "tao::Tao".to_owned(),
archetype: StructConfig::new("crate::tao::archetype::Archetype".to_owned()),
all_attributes: vec![],
all_attribute_imports: vec![],
introduced_attributes: vec![],
introduced_attribute_imports: vec![],
doc: "".to_owned(),
id: "1".to_owned(),
}
}
}
fn tao_fragment(cfg: &TaoConfig) -> AtomicFragment {
let mut imports = vec![
"std::convert::TryFrom".to_owned(),
"std::fmt".to_owned(),
"std::fmt::Debug".to_owned(),
"std::fmt::Formatter".to_owned(),
cfg.form.import.clone(),
cfg.parent_import.to_owned(),
"zamm_yin::tao::archetype::ArchetypeTrait".to_owned(),
cfg.archetype.import.clone(),
"zamm_yin::node_wrappers::debug_wrapper".to_owned(),
"zamm_yin::Wrapper".to_owned(),
"zamm_yin::node_wrappers::FinalNode".to_owned(),
];
if let Some(import) = &cfg.imports {
imports.push(import.clone());
}
if let Some(import) = &cfg.internal_name_cfg.attr_import {
imports.push(import.to_string());
}
AtomicFragment {
imports,
atom: formatdoc! {r#"
{doc}
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct {name} {{
base: FinalNode,
}}
impl Debug for {name} {{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {{
debug_wrapper("{name}", self, f)
}}
}}
impl From<usize> for {name} {{
fn from(id: usize) -> Self {{
Self {{
base: FinalNode::from(id),
}}
}}
}}
impl From<FinalNode> for {name} {{
fn from(f: FinalNode) -> Self {{
Self {{ base: f }}
}}
}}
impl<'a> TryFrom<&'a str> for {name} {{
type Error = String;
fn try_from(name: &'a str) -> Result<Self, Self::Error> {{
FinalNode::try_from(name).map(|f| Self {{ base: f }})
}}
}}
impl Wrapper for {name} {{
type BaseType = FinalNode;
fn essence(&self) -> &FinalNode {{
&self.base
}}
fn essence_mut(&mut self) -> &mut FinalNode {{
&mut self.base
}}
}}
impl<'a> ArchetypeTrait<'a> for {name} {{
type ArchetypeForm = {archetype};
type Form = {form};
const TYPE_ID: usize = {id};
const TYPE_NAME: &'static str = "{internal_name}";
const PARENT_TYPE_ID: usize = {parent}::TYPE_ID;
}}"#,
doc = cfg.doc,
name = cfg.this.name,
form = cfg.form.name,
internal_name = cfg.internal_name,
parent = cfg.parent_name,
archetype = cfg.archetype.name,
id = cfg.id,
},
}
}
fn tao_test_fragment(cfg: &TaoConfig) -> AppendedFragment {
let mut imports = vec![
"std::rc::Rc".to_owned(),
"zamm_yin::node_wrappers::CommonNodeTrait".to_owned(),
"zamm_yin::tao::archetype::ArchetypeFormTrait".to_owned(),
];
for attr_import in &cfg.all_attribute_imports {
imports.push(attr_import.clone());
}
for attr_import in &cfg.introduced_attribute_imports {
imports.push(attr_import.clone());
}
let mut test_frag = AppendedFragment::default();
let name = &cfg.this.name;
let check_type_created = new_kb_test(&mut test_frag, "check_type_created");
add_assert(
&check_type_created,
format!("{}::archetype().id()", name),
format!("{}::TYPE_ID", name),
);
add_assert(
&check_type_created,
format!(
"{name}::archetype().{getter}()",
name = name,
getter = cfg.internal_name_cfg.getter
),
format!(
"Some(Rc::from({name}::TYPE_NAME{suffix}))",
name = name,
suffix = cfg.internal_name_cfg.suffix
),
);
let from_name = new_kb_test(&mut test_frag, "from_name");
from_name
.borrow_mut()
.append(Rc::new(RefCell::new(AtomicFragment::new(formatdoc!(
r#"
let mut concept = {name}::new();
concept.{internal_name_setter}("A"{internal_name_suffix});"#,
name = name,
internal_name_setter = cfg.internal_name_cfg.setter,
internal_name_suffix = cfg.internal_name_cfg.suffix,
)))));
add_assert(
&from_name,
format!(r#"{}::try_from("A").map(|c| c.id())"#, name),
"Ok(concept.id())".to_owned(),
);
from_name
.borrow_mut()
.append(Rc::new(RefCell::new(AtomicFragment::new(format!(
r#"assert!({}::try_from("B").is_err());"#,
name
)))));
let check_type_attributes = new_kb_test(&mut test_frag, "check_type_attributes");
let mut introduced_attrs = VecFragment::new();
for attr in &cfg.introduced_attributes {
introduced_attrs.add_element_str(&attr);
}
add_assert_frags(
&check_type_attributes,
Rc::new(RefCell::new(AtomicFragment::new(format!(
"{name}::archetype().{added_attributes_f}()",
name = name,
added_attributes_f = cfg.internal_name_cfg.added_attributes
)))),
Rc::new(RefCell::new(introduced_attrs)),
);
let mut all_attrs = VecFragment::new();
for attr in &cfg.all_attributes {
all_attrs.add_element_str(&attr);
}
add_assert_frags(
&check_type_attributes,
Rc::new(RefCell::new(AtomicFragment::new(format!(
"{name}::archetype().{all_attributes_f}()",
name = name,
all_attributes_f = cfg.internal_name_cfg.all_attributes
)))),
Rc::new(RefCell::new(all_attrs)),
);
test_frag.append(Rc::new(RefCell::new(AtomicFragment {
imports,
atom: formatdoc! {r#"
#[test]
fn from_node_id() {{
initialize_kb();
let concept = {name}::new();
let concept_copy = {name}::from(concept.id());
assert_eq!(concept.id(), concept_copy.id());
}}
#[test]
fn test_wrapper_implemented() {{
initialize_kb();
let concept = {name}::new();
assert_eq!(concept.essence(), &FinalNode::from(concept.id()));
}}"#,
name = cfg.this.name,
},
})));
test_frag
}
pub fn tao_file_fragment(cfg: &TaoConfig) -> FileFragment {
let mut file = FileFragment::default();
file.set_self_import(cfg.this.import.clone());
file.append(Rc::new(RefCell::new(tao_fragment(cfg))));
file.append_test(Rc::new(RefCell::new(tao_test_fragment(cfg))));
file
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_internal_name_used() {
let code = tao_file_fragment(&TaoConfig {
internal_name_cfg: InternalNameConfig::DEFAULT,
..TaoConfig::default()
})
.generate_code();
assert!(code.contains(".set_internal_name("));
assert!(!code.contains(".set_internal_name_str("));
assert!(code.contains(".internal_name("));
assert!(!code.contains(".internal_name_str("));
assert!(code.contains(".to_owned()"));
}
#[test]
fn test_new_yin_internal_name_used() {
let code = tao_file_fragment(&TaoConfig {
internal_name_cfg: InternalNameConfig::YIN_AT_LEAST_0_1_1,
..TaoConfig::default()
})
.generate_code();
assert!(!code.contains(".set_internal_name("));
assert!(code.contains(".set_internal_name_str("));
assert!(!code.contains(".internal_name("));
assert!(code.contains(".internal_name_str("));
assert!(!code.contains(".to_owned()"));
}
}