use super::util::kb_test_function;
use crate::codegen::docstring::into_docstring;
use crate::codegen::template::basic::{
AtomicFragment, FileFragment, FunctionCallFragment, FunctionFragment, ItemDeclarationAPI,
};
use crate::codegen::{StructConfig, CODE_WIDTH};
use crate::tao::form::{Crate, CrateExtension};
use indoc::formatdoc;
use itertools::Itertools;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug, Default, Eq, PartialEq)]
pub struct Link {
pub from: StructConfig,
pub link_type: StructConfig,
pub to: StructConfig,
}
impl Link {
pub fn as_tuple(&self) -> (&str, &str, &str) {
(
self.from.name.as_str(),
self.link_type.name.as_str(),
self.to.name.as_str(),
)
}
}
#[derive(Default)]
pub struct KBInitConfig {
pub concepts_to_initialize: Vec<StructConfig>,
pub attributes: Vec<Link>,
pub yin: bool,
}
fn concept_id_fragment(concept: &StructConfig) -> AtomicFragment {
AtomicFragment {
imports: vec![concept.import.clone()],
atom: format!("{}::TYPE_ID", concept.name),
}
}
fn init_types_fragment(cfg: &KBInitConfig) -> FunctionFragment {
let mut init_fn = FunctionFragment::new("initialize_types".to_owned());
init_fn.mark_as_public();
init_fn.document("Adds all concepts to knowledge graph.".to_owned());
init_fn.add_import("zamm_yin::graph::InjectionGraph".to_owned());
init_fn.add_import("zamm_yin::graph::Graph".to_owned());
init_fn.add_import("zamm_yin::initialize_type".to_owned());
init_fn.add_import("zamm_yin::tao::archetype::ArchetypeTrait".to_owned());
init_fn.add_import("zamm_yin::tao::relation::attribute::Inherits".to_owned());
for concept in &cfg.concepts_to_initialize {
init_fn.add_import(concept.import.clone());
}
let concepts_list = cfg
.concepts_to_initialize
.iter()
.map(|c| &c.name)
.format(",\n ")
.to_string();
init_fn.append(Rc::new(RefCell::new(AtomicFragment::new(formatdoc! {"
let mut ig = InjectionGraph::new();
#[rustfmt::skip]
initialize_type!(
ig,
(
{concepts}
)
);
", concepts = concepts_list}))));
for attr in &cfg.attributes {
let mut add_edge = FunctionCallFragment::new(AtomicFragment::new("ig.add_edge".to_owned()));
add_edge.add_argument(Rc::new(RefCell::new(concept_id_fragment(&attr.from))));
add_edge.add_argument(Rc::new(RefCell::new(concept_id_fragment(&attr.link_type))));
add_edge.add_argument(Rc::new(RefCell::new(concept_id_fragment(&attr.to))));
init_fn.append(Rc::new(RefCell::new(add_edge)));
}
init_fn
}
fn max_id_fragment(cfg: &KBInitConfig) -> AtomicFragment {
let max_id_doc = into_docstring("The maximum concept ID inside the types distributed by Yin itself. App-specific type concepts should continue their numbering on top of this.", CODE_WIDTH);
let concepts_size = cfg.concepts_to_initialize.len();
let max_id = if cfg.yin {
format!("{}", concepts_size - 1) } else {
format!("zamm_yin::tao::YIN_MAX_ID + {}", concepts_size)
};
AtomicFragment::new(formatdoc! {"
{doc}
pub const YIN_MAX_ID: usize = {max_id};
", doc = max_id_doc,
max_id = max_id,
})
}
fn yin_size_test() -> FunctionFragment {
let mut f = kb_test_function("test_yin_size");
f.append(Rc::new(RefCell::new(AtomicFragment::new(formatdoc! {"
let g = InjectionGraph::new();
assert_eq!(g.size(), YIN_MAX_ID + 1);"}))));
f
}
pub fn code_init(cfg: &KBInitConfig) -> String {
let mut file = FileFragment::default();
file.append(Rc::new(RefCell::new(max_id_fragment(cfg)))); file.append(Rc::new(RefCell::new(init_types_fragment(cfg))));
if cfg.yin {
file.append_test(Rc::new(RefCell::new(yin_size_test())));
}
file.set_current_crate(Crate::current().implementation_name().unwrap());
file.generate_code()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codegen::template::basic::CodeFragment;
use crate::tao::initialize_kb;
use indoc::indoc;
#[test]
fn test_init_one_concept() {
assert_eq!(
init_types_fragment(&KBInitConfig {
concepts_to_initialize: vec![StructConfig {
name: "Me".to_owned(),
import: "crate::people::Me".to_owned(),
}],
..KBInitConfig::default()
})
.body(80),
indoc! {"
/// Adds all concepts to knowledge graph.
pub fn initialize_types() {
let mut ig = InjectionGraph::new();
#[rustfmt::skip]
initialize_type!(
ig,
(
Me
)
);
}"}
);
}
#[test]
fn test_init_multiple_concepts() {
assert_eq!(
init_types_fragment(&KBInitConfig {
concepts_to_initialize: vec![
StructConfig {
name: "Me".to_owned(),
import: "zamm_yin::people::Me".to_owned(),
},
StructConfig {
name: "You".to_owned(),
import: "crate::people::You".to_owned(),
},
StructConfig {
name: "Us".to_owned(),
import: "crate::groups::Us".to_owned(),
}
],
..KBInitConfig::default()
})
.body(80),
indoc! {"
/// Adds all concepts to knowledge graph.
pub fn initialize_types() {
let mut ig = InjectionGraph::new();
#[rustfmt::skip]
initialize_type!(
ig,
(
Me,
You,
Us
)
);
}"}
);
}
#[test]
fn test_init_additional_relations() {
assert_eq!(
init_types_fragment(&KBInitConfig {
concepts_to_initialize: vec![
StructConfig {
name: "Me".to_owned(),
import: "zamm_yin::people::Me".to_owned(),
},
StructConfig {
name: "You".to_owned(),
import: "crate::people::You".to_owned(),
},
StructConfig {
name: "Us".to_owned(),
import: "crate::groups::Us".to_owned(),
}
],
attributes: vec![
Link {
from: StructConfig::new("zamm_yin::people::Me".to_owned()),
link_type: StructConfig::new("crate::emotions::Dislike".to_owned()),
to: StructConfig::new("crate::people::You".to_owned()),
},
Link {
from: StructConfig::new("zamm_yin::people::Me".to_owned()),
link_type: StructConfig::new("crate::emotions::Like".to_owned()),
to: StructConfig::new("crate::people::Us".to_owned()),
},
],
yin: false,
})
.body(80),
indoc! {"
/// Adds all concepts to knowledge graph.
pub fn initialize_types() {
let mut ig = InjectionGraph::new();
#[rustfmt::skip]
initialize_type!(
ig,
(
Me,
You,
Us
)
);
ig.add_edge(Me::TYPE_ID, Dislike::TYPE_ID, You::TYPE_ID);
ig.add_edge(Me::TYPE_ID, Like::TYPE_ID, Us::TYPE_ID);
}"}
);
}
#[test]
fn test_init_file() {
initialize_kb();
Crate::current().set_implementation_name("zamm_yin");
let code = code_init(&KBInitConfig {
concepts_to_initialize: vec![
StructConfig {
name: "Me".to_owned(),
import: "zamm_yin::people::Me".to_owned(),
},
StructConfig {
name: "You".to_owned(),
import: "crate::people::You".to_owned(),
},
StructConfig {
name: "Us".to_owned(),
import: "crate::groups::Us".to_owned(),
},
],
yin: true,
..KBInitConfig::default()
});
assert!(code.contains("YIN_MAX_ID: usize = 2"));
assert!(!code.contains("zamm_yin"));
}
}