use super::*;
#[derive(Clone, Copy)]
pub(super) enum FaceWrite {
Create,
Edit,
}
impl FaceWrite {
pub(super) fn field_order(self) -> &'static [&'static str] {
match self {
Self::Create => CREATE_FIELD_ORDER,
Self::Edit => EDIT_FIELD_ORDER,
}
}
}
const CREATE_FIELD_ORDER: &[&str] = &[
"kind",
"preset",
"parts",
"name_zh",
"name_en",
"summary_zh",
"summary_en",
"exports",
"stable_name",
"getting_from_other_registry",
"requires",
"provides",
"runtime_checks",
"flow",
"flow_provider",
"handle_traits",
"handle_contracts",
"part_traits",
"part_contracts",
"needs_registry",
"registration_rule",
"admission",
];
const EDIT_FIELD_ORDER: &[&str] = &[
"kind",
"preset",
"parts",
"name_zh",
"name_en",
"summary_zh",
"summary_en",
"exports",
"stable_name",
"needs_registry",
"getting_from_other_registry",
"registration_rule",
"admission",
"handle_traits",
"part_traits",
"requires",
"provides",
"runtime_checks",
"flow",
"flow_provider",
];
pub(super) fn apply_module_face_values(
face: &mut FaceManifest,
values: &ModuleFaceValues<'_>,
write: FaceWrite,
) -> Result<(), String> {
for field in write.field_order() {
apply_face_value(face, values, field, write)?;
}
Ok(())
}
pub(super) fn apply_face_value(
face: &mut FaceManifest,
values: &ModuleFaceValues<'_>,
field: &str,
write: FaceWrite,
) -> Result<(), String> {
match field {
"kind" => edit_kind(face, values.kind, write),
"needs_registry" => face.edit("needs_registry", &values.needs_registry.to_string()),
"handle_traits" => apply_trait_contract(
face,
"handle_traits",
values.handle_traits,
values.handle_contracts,
),
"part_traits" => apply_trait_contract(
face,
"part_traits",
values.part_traits,
values.part_contracts,
),
"registration_rule" | "admission" => edit_required(face, field, values.value(field), write),
_ => edit_optional(face, field, values.value(field), write),
}
}
pub(super) fn edit_kind(
face: &mut FaceManifest,
kind: &str,
write: FaceWrite,
) -> Result<(), String> {
if matches!(write, FaceWrite::Create) && kind.trim().is_empty() {
return Ok(());
}
face.edit("kind", &normalize_kind_name(kind.trim()))
}
pub(super) fn edit_optional(
face: &mut FaceManifest,
field: &str,
value: &str,
write: FaceWrite,
) -> Result<(), String> {
match write {
FaceWrite::Create => {
let value = value.trim();
if value.is_empty() {
Ok(())
} else {
face.edit(field, value)
}
}
FaceWrite::Edit => face.edit(field, value),
}
}
pub(super) fn edit_required(
face: &mut FaceManifest,
field: &str,
value: &str,
write: FaceWrite,
) -> Result<(), String> {
match write {
FaceWrite::Create => face.edit(field, value.trim()),
FaceWrite::Edit => face.edit(field, value),
}
}
pub(super) fn apply_trait_contract(
face: &mut FaceManifest,
label_field: &str,
labels: &str,
contract_paths: &str,
) -> Result<(), String> {
if labels.trim().is_empty() && contract_paths.trim().is_empty() {
return Ok(());
}
let labels = if contract_paths.trim().is_empty() {
labels.trim().to_owned()
} else {
trait_names_from_paths(contract_paths)?
};
face.edit(label_field, &labels)
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! shared_face_fields {
($($field:ident = $value:expr),* $(,)?) => {
(
NewModuleFace {
$($field: $value,)*
parent: ROOT_NODE_ID,
},
ModuleFacePatch {
$($field: $value,)*
},
)
};
}
fn fixture() -> (NewModuleFace<'static>, ModuleFacePatch<'static>) {
shared_face_fields!(
module = "widget",
kind = "Widget",
preset = "NoPreset",
parts = "NoParts",
name_zh = "部件",
name_en = "Widget",
summary_zh = "摘要",
summary_en = "Summary",
exports = "a,b",
stable_name = "widget",
needs_registry = false,
getting_from_other_registry = "",
registration_rule = "ANY",
admission = "ANY",
handle_traits = "",
handle_contracts = "",
part_traits = "",
part_contracts = "",
requires = "",
provides = "widget",
runtime_checks = "",
flow = "",
flow_provider = "",
)
}
fn blank_manifest() -> FaceManifest {
FaceManifest::new(
"widget",
"Widget",
ROOT_NODE_ID,
"<root>",
"root",
"widget/widget.rs",
)
}
#[test]
fn identical_values_reach_the_same_face_from_add_and_edit() {
let (request, patch) = fixture();
let new_values = ModuleFaceValues::from_new(&request);
let patch_values = ModuleFaceValues::from_patch(&patch);
for field in CREATE_FIELD_ORDER
.iter()
.copied()
.chain(EDIT_FIELD_ORDER.iter().copied())
{
assert_eq!(
new_values.value(field),
patch_values.value(field),
"shared field `{field}`"
);
}
let mut created = blank_manifest();
apply_module_face_values(&mut created, &new_values, FaceWrite::Create)
.expect("create applies");
let mut edited = blank_manifest();
apply_module_face_values(&mut edited, &patch_values, FaceWrite::Edit)
.expect("edit applies");
assert_eq!(created.values, edited.values);
}
#[test]
fn a_blank_create_keeps_defaults_while_a_blank_edit_writes_them_through() {
let (request, patch) = fixture();
let blank_request = NewModuleFace {
preset: "",
..request
};
let blank_patch = ModuleFacePatch {
preset: "",
..patch
};
let mut created = blank_manifest();
apply_module_face_values(
&mut created,
&ModuleFaceValues::from_new(&blank_request),
FaceWrite::Create,
)
.expect("create applies");
assert_eq!(
created.values.get("preset").map(String::as_str),
Some("NoPreset")
);
let mut edited = blank_manifest();
apply_module_face_values(
&mut edited,
&ModuleFaceValues::from_patch(&blank_patch),
FaceWrite::Edit,
)
.expect("edit applies");
assert_eq!(edited.values.get("preset").map(String::as_str), Some(""));
}
}