use super::CodegenConfig;
use crate::codegen::docstring::into_docstring;
use crate::codegen::template::concept::attribute::{code_attribute, AttributeFormatConfig};
use crate::codegen::template::concept::data::{code_data_concept, DataFormatConfig};
use crate::codegen::template::concept::form::code_form;
use crate::codegen::template::concept::tao::{code_tao, TaoConfig};
use crate::codegen::StructConfig;
use crate::tao::archetype::CodegenFlags;
use crate::tao::form::data::{Data, DataExtension};
use crate::tao::form::{BuildInfo, DefinedMarker};
use crate::tao::Implement;
use heck::KebabCase;
use heck::{CamelCase, SnakeCase};
use itertools::Itertools;
use std::convert::TryFrom;
use std::rc::Rc;
use zamm_yin::node_wrappers::CommonNodeTrait;
use zamm_yin::tao::archetype::{Archetype, ArchetypeFormTrait, ArchetypeTrait, AttributeArchetype};
use zamm_yin::tao::form::{Form, FormTrait};
use zamm_yin::tao::relation::attribute::Attribute;
fn in_own_submodule(target: &Archetype) -> bool {
target.root_node_logic_activated() || !target.child_archetypes().is_empty()
}
fn ancestor_path(target: &Archetype, separator: &str, force_own_module: bool) -> String {
let build_info = BuildInfo::from(target.id());
match build_info.import_path() {
Some(existing_path) => {
let modules = existing_path.split("::").collect::<Vec<&str>>();
modules
.iter()
.skip(1)
.take(modules.len() - 2)
.format(separator)
.to_string()
}
None => {
let parent_path = if target.root_node_logic_activated() {
None
} else {
Some(ancestor_path(
&target.parents().first().unwrap(),
separator,
true,
))
};
let target_name = target
.internal_name()
.unwrap()
.as_str()
.to_snake_case()
.to_ascii_lowercase();
if force_own_module || in_own_submodule(target) {
match parent_path {
Some(actual_parent_path) => {
format!("{}{}{}", actual_parent_path, separator, target_name)
}
None => target_name,
}
} else {
parent_path.unwrap() }
}
}
}
pub fn file_path(target: &Archetype) -> String {
let snake_name = target
.internal_name()
.unwrap()
.as_str()
.to_snake_case()
.to_ascii_lowercase();
format!(
"src/{}/{}_form.rs",
ancestor_path(target, "/", target.force_own_module()),
snake_name
)
}
fn import_path(target: &Archetype, force_own_module: bool, yin_override: bool) -> String {
let build_info = BuildInfo::from(target.id());
match build_info.import_path() {
Some(existing_path) => (*existing_path).clone(),
None => {
let yin_crate = if build_info.crate_name().is_some() {
(*build_info.crate_name().unwrap()).clone()
} else if yin_override || target.is_newly_defined() {
"crate".to_owned()
} else {
"zamm_yin".to_owned()
};
let struct_name = target.internal_name().unwrap().as_str().to_camel_case();
format!(
"{}::{}::{}",
yin_crate,
ancestor_path(&target, "::", force_own_module),
struct_name
)
}
}
}
fn concept_to_struct(target: &Archetype, yin_override: bool) -> StructConfig {
let build_info = BuildInfo::from(target.id());
let name = build_info
.implementation_name()
.unwrap_or_else(|| Rc::new(target.internal_name().unwrap().as_str().to_camel_case()));
StructConfig {
name: (*name).clone(),
import: import_path(target, target.force_own_module(), yin_override),
}
}
fn or_form_default(archetype: Archetype) -> Archetype {
if archetype.root_node_logic_activated() {
Archetype::try_from(Form::TYPE_NAME).unwrap() } else {
archetype
}
}
fn activate_archetype(target: &Archetype, parent: &Archetype) -> bool {
target == &Attribute::archetype().as_archetype()
|| parent.has_ancestor(Attribute::archetype().as_archetype())
|| target.attribute_logic_activated()
}
fn activate_data(target: &Archetype) -> bool {
target.has_ancestor(Data::archetype().as_archetype()) || target.data_logic_activated()
}
fn generic_config(
request: &Implement,
target: &Archetype,
parent: &Archetype,
codegen_cfg: &CodegenConfig,
) -> TaoConfig {
let this = concept_to_struct(&target, codegen_cfg.yin);
let internal_name = this.name.to_kebab_case();
let form = if target.root_node_logic_activated() {
concept_to_struct(&Form::archetype(), codegen_cfg.yin)
} else {
this.clone()
};
let impl_cfg = request.config().unwrap();
let doc = match &impl_cfg.doc {
Some(d) => format!("\n{}", into_docstring(&d, 0)),
None => String::new(),
};
let id = if codegen_cfg.yin {
format!("{}", impl_cfg.id)
} else {
format!("YIN_MAX_ID + {}", impl_cfg.id)
};
let yin_crate = if codegen_cfg.yin { "crate" } else { "zamm_yin" };
let imports = if codegen_cfg.yin {
None
} else {
Some("zamm_yin::tao::YIN_MAX_ID".to_owned())
};
let parent_struct = concept_to_struct(parent, codegen_cfg.yin);
let all_attribute_structs: Vec<StructConfig> = target
.attribute_archetypes()
.iter()
.map(|a| concept_to_struct(&a.as_archetype(), codegen_cfg.yin))
.collect();
let introduced_attribute_structs: Vec<StructConfig> = target
.introduced_attribute_archetypes()
.iter()
.map(|a| concept_to_struct(&a.as_archetype(), codegen_cfg.yin))
.collect();
let all_attributes = format!(
"vec![{}]",
all_attribute_structs
.iter()
.map(|s| format!("{}::archetype()", s.name))
.format(", ")
);
let all_attribute_imports = all_attribute_structs
.iter()
.map(|s| s.import.clone())
.collect();
let introduced_attributes = format!(
"vec![{}]",
introduced_attribute_structs
.iter()
.map(|s| format!("{}::archetype()", s.name))
.format(", ")
);
let introduced_attribute_imports = introduced_attribute_structs
.iter()
.map(|s| s.import.clone())
.collect();
let archetype_name = if activate_archetype(target, parent) {
"AttributeArchetype".to_owned()
} else {
"Archetype".to_owned()
};
TaoConfig {
yin_crate: yin_crate.to_owned(),
imports,
this,
internal_name,
form,
parent_name: parent_struct.name,
parent_import: parent_struct.import,
all_attributes,
all_attribute_imports,
introduced_attributes,
introduced_attribute_imports,
archetype_name,
doc,
id,
}
}
fn attribute_config(
base_cfg: TaoConfig,
target: &Archetype,
codegen_cfg: &CodegenConfig,
) -> AttributeFormatConfig {
let target_attr = AttributeArchetype::from(target.id());
let owner_type_concept = target_attr.owner_archetype();
let value_type_concept = target_attr.value_archetype();
let owner_type = concept_to_struct(&owner_type_concept, codegen_cfg.yin);
let value_type = concept_to_struct(&value_type_concept, codegen_cfg.yin);
let owner_form = concept_to_struct(&or_form_default(owner_type_concept), codegen_cfg.yin);
let value_form = concept_to_struct(&or_form_default(value_type_concept), codegen_cfg.yin);
AttributeFormatConfig {
tao_cfg: base_cfg,
owner_type,
owner_form,
value_type,
value_form,
}
}
fn data_config(base_cfg: TaoConfig, target: &Archetype) -> DataFormatConfig {
DataFormatConfig {
tao_cfg: base_cfg,
rust_primitive_name: target.rust_primitive().unwrap(),
default_value: target.default_value().unwrap(),
}
}
fn primary_parent(target: &Archetype) -> Archetype {
*target.parents().first().unwrap()
}
pub fn code(request: Implement, codegen_cfg: &CodegenConfig) -> String {
let target = request.target().unwrap();
let parent = primary_parent(&target);
let base_cfg = generic_config(&request, &target, &parent, codegen_cfg);
if target.root_node_logic_activated() {
code_tao(&base_cfg)
} else if activate_archetype(&target, &parent) {
code_attribute(&attribute_config(base_cfg, &target, codegen_cfg))
} else if activate_data(&target) {
code_data_concept(&data_config(base_cfg, &target))
} else {
code_form(&base_cfg)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tao::initialize_kb;
use crate::tao::ImplementConfig;
use indoc::indoc;
use zamm_yin::tao::relation::attribute::{Attribute, Owner};
use zamm_yin::tao::Tao;
#[test]
fn own_submodule_tao() {
initialize_kb();
assert!(in_own_submodule(&Tao::archetype()));
}
#[test]
fn own_submodule_attributes() {
initialize_kb();
assert!(in_own_submodule(&Attribute::archetype().as_archetype()));
}
#[test]
fn own_submodule_nested() {
initialize_kb();
assert!(!in_own_submodule(&Owner::archetype().as_archetype()));
}
#[test]
fn folder_path_tao() {
initialize_kb();
assert_eq!(file_path(&Tao::archetype()), "src/tao/tao_form.rs");
}
#[test]
fn folder_path_attributes() {
initialize_kb();
assert_eq!(
file_path(&Attribute::archetype().as_archetype()),
"src/tao/relation/attribute/attribute_form.rs"
);
}
#[test]
fn folder_path_nested() {
initialize_kb();
assert_eq!(
file_path(&Owner::archetype().as_archetype()),
"src/tao/relation/attribute/owner_form.rs"
);
}
#[test]
fn folder_path_forced_own_module() {
initialize_kb();
let mut owner = Owner::archetype();
owner.mark_own_module();
assert_eq!(
file_path(&owner.as_archetype()),
"src/tao/relation/attribute/owner/owner_form.rs"
);
}
#[test]
fn folder_path_custom_module() {
initialize_kb();
BuildInfo::from(Attribute::TYPE_ID)
.set_import_path("zamm_yin::tao::newfangled::module::attribute::Attribute");
let mut owner = Owner::archetype();
owner.mark_own_module();
assert_eq!(
file_path(&owner.as_archetype()),
"src/tao/newfangled/module/attribute/owner/owner_form.rs"
);
}
#[test]
fn import_path_tao() {
initialize_kb();
assert_eq!(
import_path(&Tao::archetype(), false, false),
"zamm_yin::tao::Tao"
);
}
#[test]
fn import_path_attributes() {
initialize_kb();
assert_eq!(
import_path(&Attribute::archetype().as_archetype(), false, false),
"zamm_yin::tao::relation::attribute::Attribute"
);
}
#[test]
fn import_path_nested() {
initialize_kb();
assert_eq!(
import_path(&Owner::archetype().as_archetype(), false, false),
"zamm_yin::tao::relation::attribute::Owner"
);
}
#[test]
fn import_path_forced_own_module() {
initialize_kb();
assert_eq!(
import_path(&Owner::archetype().as_archetype(), true, false),
"zamm_yin::tao::relation::attribute::owner::Owner"
);
}
#[test]
fn import_path_newly_defined() {
initialize_kb();
let mut owner = Owner::archetype();
owner.mark_newly_defined();
assert_eq!(
import_path(&owner.as_archetype(), false, false),
"crate::tao::relation::attribute::Owner"
);
}
#[test]
fn import_path_custom_module() {
initialize_kb();
BuildInfo::from(Attribute::TYPE_ID)
.set_import_path("zamm_yin::tao::newfangled::module::attribute::Attribute");
let mut owner = Owner::archetype();
owner.mark_own_module();
owner.mark_newly_defined();
assert_eq!(
import_path(&owner.as_archetype(), true, false),
"crate::tao::newfangled::module::attribute::owner::Owner"
);
}
#[test]
fn import_path_custom_crate() {
initialize_kb();
BuildInfo::from(Attribute::TYPE_ID)
.set_import_path("zamm_yin::tao::newfangled::module::attribute::Attribute");
let mut owner = Owner::archetype();
owner.mark_own_module();
owner.mark_newly_defined();
BuildInfo::from(owner.id()).set_crate_name("mycrate");
assert_eq!(
import_path(&owner.as_archetype(), true, false),
"mycrate::tao::newfangled::module::attribute::owner::Owner"
);
}
#[test]
fn import_path_multiple_descendants() {
initialize_kb();
let mut type1 = Tao::archetype().individuate_as_archetype();
type1.set_internal_name("hello".to_owned());
type1.mark_newly_defined();
let mut type2 = type1.individuate_as_archetype();
type2.set_internal_name("world".to_owned());
type2.mark_newly_defined();
assert_eq!(
import_path(&type2, true, false),
"crate::tao::hello::world::World"
);
}
#[test]
fn import_path_custom_root() {
initialize_kb();
let mut root = Tao::archetype().individuate_as_archetype();
root.set_internal_name("my-root".to_owned());
root.mark_newly_defined();
root.activate_root_node_logic();
assert_eq!(import_path(&root, false, false), "crate::my_root::MyRoot");
}
#[test]
fn struct_config_tao() {
initialize_kb();
assert_eq!(
concept_to_struct(&Tao::archetype(), false),
StructConfig {
name: "Tao".to_owned(),
import: "zamm_yin::tao::Tao".to_owned(),
}
);
}
#[test]
fn struct_config_attributes() {
initialize_kb();
assert_eq!(
concept_to_struct(&Attribute::archetype().as_archetype(), false),
StructConfig {
name: "Attribute".to_owned(),
import: "zamm_yin::tao::relation::attribute::Attribute".to_owned(),
}
);
}
#[test]
fn struct_config_nested() {
initialize_kb();
assert_eq!(
concept_to_struct(&Owner::archetype().as_archetype(), false),
StructConfig {
name: "Owner".to_owned(),
import: "zamm_yin::tao::relation::attribute::Owner".to_owned(),
}
);
}
#[test]
fn struct_config_forced_own_module() {
initialize_kb();
let mut owner = Owner::archetype();
owner.mark_own_module();
assert_eq!(
concept_to_struct(&owner.as_archetype(), false),
StructConfig {
name: "Owner".to_owned(),
import: "zamm_yin::tao::relation::attribute::owner::Owner".to_owned(),
}
);
}
#[test]
fn struct_config_newly_defined() {
initialize_kb();
let mut owner = Owner::archetype();
owner.mark_newly_defined();
assert_eq!(
concept_to_struct(&owner.as_archetype(), false),
StructConfig {
name: "Owner".to_owned(),
import: "crate::tao::relation::attribute::Owner".to_owned(),
}
);
}
#[test]
fn struct_config_override() {
initialize_kb();
let mut tao_build = BuildInfo::from(Tao::TYPE_ID);
tao_build.set_implementation_name("TaoStruct");
tao_build.set_import_path("crate::TaoStruct");
assert_eq!(
concept_to_struct(&Tao::archetype(), false),
StructConfig {
name: "TaoStruct".to_owned(),
import: "crate::TaoStruct".to_owned(),
}
);
}
#[test]
fn test_default_no_activations() {
initialize_kb();
let mut target = Tao::archetype().individuate_as_archetype();
target.set_internal_name("MyAttrType".to_owned());
target.mark_newly_defined();
let mut implement = Implement::individuate();
implement.set_target(target.as_archetype());
implement.set_config(ImplementConfig::default());
let cfg = generic_config(
&implement,
&target.as_archetype(),
&primary_parent(&target),
&CodegenConfig::default(),
);
assert!(!target.root_node_logic_activated());
assert!(!activate_archetype(&target, &primary_parent(&target)));
assert!(!activate_data(&target));
assert!(cfg.id.contains("YIN_MAX_ID"));
}
#[test]
fn code_cfg_for_yin() {
initialize_kb();
let mut target = Tao::archetype().individuate_as_archetype();
target.set_internal_name("MyDataType".to_owned());
target.mark_newly_defined();
let mut implement = Implement::individuate();
implement.set_target(target.as_archetype());
implement.set_config(ImplementConfig::default());
let cfg = generic_config(
&implement,
&target.as_archetype(),
&primary_parent(&target),
&CodegenConfig {
yin: true,
..CodegenConfig::default()
},
);
assert!(!cfg.id.contains("YIN_MAX_ID"));
}
#[test]
fn test_default_doc_newline() {
initialize_kb();
let mut target = Tao::archetype().individuate_as_archetype();
target.set_internal_name("MyAttrType".to_owned());
target.mark_newly_defined();
let mut implement = Implement::individuate();
implement.set_target(target.as_archetype());
implement.set_config(ImplementConfig {
doc: Some("One.\n\nTwo.".to_owned()),
..ImplementConfig::default()
});
let cfg = generic_config(
&implement,
&target.as_archetype(),
&primary_parent(&target),
&CodegenConfig::default(),
);
assert_eq!(
cfg.doc.trim(),
indoc! {r"
/// One.
///
/// Two.
"}
.trim()
);
}
#[test]
fn code_cfg_for_root_node_activated() {
initialize_kb();
let mut target = Tao::archetype().individuate_as_archetype();
target.set_internal_name("MyRoot".to_owned());
target.mark_newly_defined();
target.activate_root_node_logic();
assert!(target.root_node_logic_activated());
assert!(!activate_archetype(&target, &primary_parent(&target)));
assert!(!activate_data(&target));
}
#[test]
fn code_cfg_for_attribute_activated() {
initialize_kb();
let mut target = AttributeArchetype::from(Tao::archetype().individuate_as_archetype().id());
target.set_internal_name("MyAttrType".to_owned());
target.mark_newly_defined();
target.activate_attribute_logic();
target.set_owner_archetype(Tao::archetype());
target.set_value_archetype(Form::archetype());
let mut implement = Implement::individuate();
implement.set_target(target.as_archetype());
implement.set_config(ImplementConfig::default());
let parent = primary_parent(&target.as_archetype());
let codegen_cfg = CodegenConfig::default();
let attr_cfg = attribute_config(
generic_config(&implement, &target.as_archetype(), &parent, &codegen_cfg),
&target.as_archetype(),
&codegen_cfg,
);
assert!(!target.root_node_logic_activated());
assert!(activate_archetype(&target.as_archetype(), &parent));
assert!(!activate_data(&target.as_archetype()));
assert_eq!(attr_cfg.owner_type.name, "Tao".to_owned());
assert_eq!(attr_cfg.value_type.name, "Form".to_owned());
assert_eq!(
attr_cfg.tao_cfg.archetype_name,
"AttributeArchetype".to_owned()
);
}
#[test]
fn code_cfg_for_data_activated() {
initialize_kb();
let mut target = Tao::archetype().individuate_as_archetype();
target.set_internal_name("MyDataType".to_owned());
target.mark_newly_defined();
target.activate_data_logic();
assert!(!target.root_node_logic_activated());
assert!(!activate_archetype(&target, &primary_parent(&target)));
assert!(activate_data(&target));
}
}