use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResourceTypeDetail {
pub name: String,
pub iri: String,
pub label: Option<String>,
pub data_model: String,
pub representation: Option<Representation>,
pub super_types: Vec<String>,
pub fields: Vec<Field>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Field {
pub name: String,
pub iri: String,
pub label: Option<String>,
pub value_type: ValueType,
pub link_target: Option<String>,
pub cardinality: Cardinality,
pub is_builtin: bool,
pub data_model: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValueType {
Text,
Integer,
Decimal,
Boolean,
Date,
Time,
Uri,
Color,
Geoname,
ListItem,
Link,
StillImage,
MovingImage,
Audio,
Document,
Archive,
Other(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cardinality {
One,
ZeroOrOne,
ZeroOrMore,
OneOrMore,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Representation {
StillImage,
MovingImage,
Audio,
Document,
Archive,
Text,
}
impl fmt::Display for Cardinality {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Cardinality::One => f.write_str("1"),
Cardinality::ZeroOrOne => f.write_str("0-1"),
Cardinality::ZeroOrMore => f.write_str("0-n"),
Cardinality::OneOrMore => f.write_str("1-n"),
}
}
}
impl fmt::Display for Representation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Representation::StillImage => f.write_str("still-image"),
Representation::MovingImage => f.write_str("moving-image"),
Representation::Audio => f.write_str("audio"),
Representation::Document => f.write_str("document"),
Representation::Archive => f.write_str("archive"),
Representation::Text => f.write_str("text"),
}
}
}
impl ValueType {
pub fn as_token(&self) -> &str {
match self {
ValueType::Text => "text",
ValueType::Integer => "integer",
ValueType::Decimal => "decimal",
ValueType::Boolean => "boolean",
ValueType::Date => "date",
ValueType::Time => "time",
ValueType::Uri => "uri",
ValueType::Color => "color",
ValueType::Geoname => "geoname",
ValueType::ListItem => "list-item",
ValueType::Link => "link",
ValueType::StillImage => "still-image",
ValueType::MovingImage => "moving-image",
ValueType::Audio => "audio",
ValueType::Document => "document",
ValueType::Archive => "archive",
ValueType::Other(s) => s.as_str(),
}
}
}
impl fmt::Display for ValueType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_token())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resource_type_detail_full_construction_and_equality() {
let detail = ResourceTypeDetail {
name: "manuscript".into(),
iri: "http://api.dasch.swiss/ontology/0801/beol/v2#manuscript".into(),
label: Some("Manuscript".into()),
data_model: "beol".into(),
representation: Some(Representation::StillImage),
super_types: vec!["writtenSource".into()],
fields: vec![Field {
name: "hasTitle".into(),
iri: "http://api.dasch.swiss/ontology/0801/beol/v2#hasTitle".into(),
label: Some("Title".into()),
value_type: ValueType::Text,
link_target: None,
cardinality: Cardinality::OneOrMore,
is_builtin: false,
data_model: Some("beol".into()),
}],
};
let cloned = detail.clone();
assert_eq!(detail, cloned);
assert_eq!(detail.name, "manuscript");
assert_eq!(
detail.iri,
"http://api.dasch.swiss/ontology/0801/beol/v2#manuscript"
);
assert_eq!(detail.label.as_deref(), Some("Manuscript"));
assert_eq!(detail.data_model, "beol");
assert_eq!(detail.representation, Some(Representation::StillImage));
assert_eq!(detail.super_types, vec!["writtenSource"]);
assert_eq!(detail.fields.len(), 1);
}
#[test]
fn resource_type_detail_minimal_none_variants() {
let detail = ResourceTypeDetail {
name: "Thing".into(),
iri: "http://api.dasch.swiss/ontology/0000/minimal/v2#Thing".into(),
label: None,
data_model: "minimal".into(),
representation: None,
super_types: vec![],
fields: vec![],
};
let cloned = detail.clone();
assert_eq!(detail, cloned);
assert_eq!(detail.label, None);
assert_eq!(detail.representation, None);
assert!(detail.super_types.is_empty());
assert!(detail.fields.is_empty());
}
#[test]
fn field_full_construction_and_equality() {
let field = Field {
name: "hasAuthor".into(),
iri: "http://api.dasch.swiss/ontology/0801/beol/v2#hasAuthor".into(),
label: Some("Author".into()),
value_type: ValueType::Link,
link_target: Some("person".into()),
cardinality: Cardinality::ZeroOrMore,
is_builtin: false,
data_model: Some("beol".into()),
};
let cloned = field.clone();
assert_eq!(field, cloned);
assert_eq!(field.name, "hasAuthor");
assert_eq!(
field.iri,
"http://api.dasch.swiss/ontology/0801/beol/v2#hasAuthor"
);
assert_eq!(field.label.as_deref(), Some("Author"));
assert_eq!(field.value_type, ValueType::Link);
assert_eq!(field.link_target.as_deref(), Some("person"));
assert_eq!(field.cardinality, Cardinality::ZeroOrMore);
assert!(!field.is_builtin);
assert_eq!(field.data_model.as_deref(), Some("beol"));
}
#[test]
fn field_minimal_none_variants() {
let field = Field {
name: "arkUrl".into(),
iri: "http://api.knora.org/ontology/knora-api/v2#arkUrl".into(),
label: None,
value_type: ValueType::Uri,
link_target: None,
cardinality: Cardinality::One,
is_builtin: true,
data_model: None,
};
let cloned = field.clone();
assert_eq!(field, cloned);
assert_eq!(field.label, None);
assert_eq!(field.link_target, None);
assert!(field.is_builtin);
assert_eq!(field.data_model, None);
}
#[test]
fn cardinality_display_one() {
assert_eq!(Cardinality::One.to_string(), "1");
}
#[test]
fn cardinality_display_zero_or_one() {
assert_eq!(Cardinality::ZeroOrOne.to_string(), "0-1");
}
#[test]
fn cardinality_display_zero_or_more() {
assert_eq!(Cardinality::ZeroOrMore.to_string(), "0-n");
}
#[test]
fn cardinality_display_one_or_more() {
assert_eq!(Cardinality::OneOrMore.to_string(), "1-n");
}
#[test]
fn representation_display_still_image() {
assert_eq!(Representation::StillImage.to_string(), "still-image");
}
#[test]
fn representation_display_moving_image() {
assert_eq!(Representation::MovingImage.to_string(), "moving-image");
}
#[test]
fn representation_display_audio() {
assert_eq!(Representation::Audio.to_string(), "audio");
}
#[test]
fn representation_display_document() {
assert_eq!(Representation::Document.to_string(), "document");
}
#[test]
fn representation_display_archive() {
assert_eq!(Representation::Archive.to_string(), "archive");
}
#[test]
fn representation_display_text() {
assert_eq!(Representation::Text.to_string(), "text");
}
#[test]
fn value_type_display_text() {
assert_eq!(ValueType::Text.to_string(), "text");
}
#[test]
fn value_type_display_integer() {
assert_eq!(ValueType::Integer.to_string(), "integer");
}
#[test]
fn value_type_display_decimal() {
assert_eq!(ValueType::Decimal.to_string(), "decimal");
}
#[test]
fn value_type_display_boolean() {
assert_eq!(ValueType::Boolean.to_string(), "boolean");
}
#[test]
fn value_type_display_date() {
assert_eq!(ValueType::Date.to_string(), "date");
}
#[test]
fn value_type_display_time() {
assert_eq!(ValueType::Time.to_string(), "time");
}
#[test]
fn value_type_display_uri() {
assert_eq!(ValueType::Uri.to_string(), "uri");
}
#[test]
fn value_type_display_color() {
assert_eq!(ValueType::Color.to_string(), "color");
}
#[test]
fn value_type_display_geoname() {
assert_eq!(ValueType::Geoname.to_string(), "geoname");
}
#[test]
fn value_type_display_list_item() {
assert_eq!(ValueType::ListItem.to_string(), "list-item");
}
#[test]
fn value_type_display_link() {
assert_eq!(ValueType::Link.to_string(), "link");
}
#[test]
fn value_type_display_still_image() {
assert_eq!(ValueType::StillImage.to_string(), "still-image");
}
#[test]
fn value_type_display_moving_image() {
assert_eq!(ValueType::MovingImage.to_string(), "moving-image");
}
#[test]
fn value_type_display_audio() {
assert_eq!(ValueType::Audio.to_string(), "audio");
}
#[test]
fn value_type_display_document() {
assert_eq!(ValueType::Document.to_string(), "document");
}
#[test]
fn value_type_display_archive() {
assert_eq!(ValueType::Archive.to_string(), "archive");
}
#[test]
fn value_type_display_other_verbatim() {
assert_eq!(
ValueType::Other("text-file".into()).to_string(),
"text-file"
);
}
#[test]
fn value_type_as_token_matches_display_named_variants() {
let cases = [
ValueType::Text,
ValueType::Integer,
ValueType::Decimal,
ValueType::Boolean,
ValueType::Date,
ValueType::Time,
ValueType::Uri,
ValueType::Color,
ValueType::Geoname,
ValueType::ListItem,
ValueType::Link,
ValueType::StillImage,
ValueType::MovingImage,
ValueType::Audio,
ValueType::Document,
ValueType::Archive,
];
for vt in &cases {
assert_eq!(
vt.as_token(),
vt.to_string(),
"as_token must match Display for {:?}",
vt
);
}
}
#[test]
fn value_type_as_token_still_image() {
assert_eq!(ValueType::StillImage.as_token(), "still-image");
}
#[test]
fn value_type_as_token_list_item() {
assert_eq!(ValueType::ListItem.as_token(), "list-item");
}
#[test]
fn value_type_as_token_moving_image() {
assert_eq!(ValueType::MovingImage.as_token(), "moving-image");
}
#[test]
fn value_type_as_token_other_borrows_string() {
let vt = ValueType::Other("geom".into());
assert_eq!(vt.as_token(), "geom");
assert_eq!(vt.as_token(), vt.to_string());
}
#[test]
fn link_field_has_link_target_some() {
let field = Field {
name: "hasAuthor".into(),
iri: "http://api.dasch.swiss/ontology/0801/beol/v2#hasAuthor".into(),
label: Some("Author".into()),
value_type: ValueType::Link,
link_target: Some("person".into()),
cardinality: Cardinality::ZeroOrMore,
is_builtin: false,
data_model: Some("beol".into()),
};
assert_eq!(field.value_type, ValueType::Link);
assert!(
field.link_target.is_some(),
"a Link field must have link_target == Some(...)"
);
}
#[test]
fn non_link_field_has_link_target_none() {
let field = Field {
name: "hasTitle".into(),
iri: "http://api.dasch.swiss/ontology/0801/beol/v2#hasTitle".into(),
label: Some("Title".into()),
value_type: ValueType::Text,
link_target: None,
cardinality: Cardinality::OneOrMore,
is_builtin: false,
data_model: Some("beol".into()),
};
assert_ne!(field.value_type, ValueType::Link);
assert!(
field.link_target.is_none(),
"a non-Link field must have link_target == None"
);
}
}