fix(domain): make FieldType::from_parts a strict inverse; reject stray bindings
This commit is contained in:
@@ -10,8 +10,14 @@ pub enum FieldType {
|
||||
Integer,
|
||||
Date,
|
||||
Boolean,
|
||||
Term { vocabulary_id: VocabularyId },
|
||||
Authority { kind: Option<AuthorityKind> },
|
||||
Term {
|
||||
vocabulary_id: VocabularyId,
|
||||
},
|
||||
/// An authority reference. `kind: None` accepts any authority kind;
|
||||
/// `Some(kind)` constrains to that kind.
|
||||
Authority {
|
||||
kind: Option<AuthorityKind>,
|
||||
},
|
||||
}
|
||||
|
||||
impl FieldType {
|
||||
@@ -37,21 +43,30 @@ impl FieldType {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconstruct from the stored columns. `None` for an unknown or inconsistent combo
|
||||
/// (e.g. `term` without a vocabulary).
|
||||
/// Reconstruct from the stored columns. Returns `None` for an unknown
|
||||
/// discriminant or an inconsistent combination — a scalar type carrying any
|
||||
/// binding, a `term` without a vocabulary (or with an authority kind), or an
|
||||
/// `authority` carrying a vocabulary.
|
||||
pub fn from_parts(
|
||||
data_type: &str,
|
||||
vocabulary_id: Option<VocabularyId>,
|
||||
authority_kind: Option<AuthorityKind>,
|
||||
) -> Option<Self> {
|
||||
let scalar = vocabulary_id.is_none() && authority_kind.is_none();
|
||||
|
||||
match data_type {
|
||||
"text" => Some(FieldType::Text),
|
||||
"localized_text" => Some(FieldType::LocalizedText),
|
||||
"integer" => Some(FieldType::Integer),
|
||||
"date" => Some(FieldType::Date),
|
||||
"boolean" => Some(FieldType::Boolean),
|
||||
"term" => vocabulary_id.map(|vocabulary_id| FieldType::Term { vocabulary_id }),
|
||||
"authority" => Some(FieldType::Authority {
|
||||
"text" if scalar => Some(FieldType::Text),
|
||||
"localized_text" if scalar => Some(FieldType::LocalizedText),
|
||||
"integer" if scalar => Some(FieldType::Integer),
|
||||
"date" if scalar => Some(FieldType::Date),
|
||||
"boolean" if scalar => Some(FieldType::Boolean),
|
||||
"term" => match vocabulary_id {
|
||||
Some(vocabulary_id) if authority_kind.is_none() => {
|
||||
Some(FieldType::Term { vocabulary_id })
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
"authority" if vocabulary_id.is_none() => Some(FieldType::Authority {
|
||||
kind: authority_kind,
|
||||
}),
|
||||
_ => None,
|
||||
@@ -117,4 +132,24 @@ mod tests {
|
||||
fn unknown_type_is_none() {
|
||||
assert_eq!(FieldType::from_parts("blob", None, None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stray_binding_on_scalar_type_is_rejected() {
|
||||
let v = VocabularyId::new();
|
||||
assert_eq!(FieldType::from_parts("text", Some(v), None), None);
|
||||
assert_eq!(
|
||||
FieldType::from_parts("boolean", None, Some(AuthorityKind::Person)),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn term_or_authority_with_wrong_binding_is_rejected() {
|
||||
let v = VocabularyId::new();
|
||||
assert_eq!(
|
||||
FieldType::from_parts("term", Some(v), Some(AuthorityKind::Person)),
|
||||
None
|
||||
);
|
||||
assert_eq!(FieldType::from_parts("authority", Some(v), None), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user