use core::fmt;
use openbim_core::Detected;
use crate::{IdsVersion, Occurrence};
#[derive(Debug, Clone, PartialEq)]
pub struct Ids {
pub version: Detected<IdsVersion>,
pub info: Info,
pub specifications: Vec<Specification>,
}
impl Ids {
#[must_use]
pub fn new(info: Info) -> Ids {
Ids {
version: Detected::Declared(IdsVersion::Ids1_0),
info,
specifications: Vec::new(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Info {
pub title: String,
pub copyright: Option<String>,
pub version: Option<String>,
pub description: Option<String>,
pub author: Option<String>,
pub date: Option<String>,
pub purpose: Option<String>,
pub milestone: Option<String>,
}
impl Info {
#[must_use]
pub fn new(title: impl Into<String>) -> Info {
Info {
title: title.into(),
..Info::default()
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Specification {
pub name: String,
pub ifc_versions: Vec<IfcVersion>,
pub identifier: Option<String>,
pub description: Option<String>,
pub instructions: Option<String>,
pub applicability: Applicability,
pub requirements: Option<Requirements>,
}
impl Specification {
#[must_use]
pub fn new(
name: impl Into<String>,
ifc_versions: impl IntoIterator<Item = IfcVersion>,
) -> Self {
Specification {
name: name.into(),
ifc_versions: ifc_versions.into_iter().collect(),
identifier: None,
description: None,
instructions: None,
applicability: Applicability::new(Occurrence::Optional),
requirements: None,
}
}
pub fn require(&mut self, requirement: impl Into<Requirement>) -> &mut Self {
self.requirements
.get_or_insert_with(Requirements::default)
.facets
.push(requirement.into());
self
}
#[must_use]
pub fn occurrence(&self) -> Occurrence {
self.applicability.occurrence()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum IfcVersion {
Ifc2x3,
Ifc4,
Ifc4x3Add2,
}
impl IfcVersion {
pub const ALL: [IfcVersion; 3] = [IfcVersion::Ifc2x3, IfcVersion::Ifc4, IfcVersion::Ifc4x3Add2];
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
IfcVersion::Ifc2x3 => "IFC2X3",
IfcVersion::Ifc4 => "IFC4",
IfcVersion::Ifc4x3Add2 => "IFC4X3_ADD2",
}
}
#[must_use]
pub fn from_token(token: &str) -> Option<IfcVersion> {
IfcVersion::ALL.into_iter().find(|v| v.as_str() == token)
}
}
impl fmt::Display for IfcVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Applicability {
pub min_occurs: u32,
pub max_occurs: Option<u32>,
pub facets: Vec<Facet>,
}
impl Applicability {
#[must_use]
pub fn new(occurrence: Occurrence) -> Applicability {
let mut applicability = Applicability {
min_occurs: 1,
max_occurs: None,
facets: Vec::new(),
};
applicability.set_occurrence(occurrence);
applicability
}
pub fn set_occurrence(&mut self, occurrence: Occurrence) -> &mut Self {
(self.min_occurs, self.max_occurs) = match occurrence {
Occurrence::Required => (1, None),
Occurrence::Optional => (0, None),
Occurrence::Prohibited => (0, Some(0)),
};
self
}
#[must_use]
pub fn occurrence(&self) -> Occurrence {
Occurrence::from_xs_occurs(self.min_occurs, self.max_occurs)
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Requirements {
pub description: Option<String>,
pub facets: Vec<Requirement>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Requirement {
pub facet: Facet,
pub occurrence: Occurrence,
pub uri: Option<String>,
pub instructions: Option<String>,
}
impl Requirement {
#[must_use]
pub fn new(facet: impl Into<Facet>) -> Requirement {
Requirement::with_occurrence(facet, Occurrence::Required)
}
#[must_use]
pub fn with_occurrence(facet: impl Into<Facet>, occurrence: Occurrence) -> Requirement {
Requirement {
facet: facet.into(),
occurrence,
uri: None,
instructions: None,
}
}
}
impl<F: Into<Facet>> From<F> for Requirement {
fn from(facet: F) -> Requirement {
Requirement::new(facet)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Facet {
Entity(Entity),
PartOf(PartOf),
Classification(Classification),
Attribute(Attribute),
Property(Property),
Material(Material),
}
macro_rules! facet_from {
($($kind:ident),*) => {$(
impl From<$kind> for Facet {
fn from(facet: $kind) -> Facet {
Facet::$kind(facet)
}
}
)*};
}
facet_from!(
Entity,
PartOf,
Classification,
Attribute,
Property,
Material
);
impl Facet {
#[must_use]
pub fn kind(&self) -> &'static str {
match self {
Facet::Entity(_) => "entity",
Facet::PartOf(_) => "partOf",
Facet::Classification(_) => "classification",
Facet::Attribute(_) => "attribute",
Facet::Property(_) => "property",
Facet::Material(_) => "material",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Entity {
pub name: Value,
pub predefined_type: Option<Value>,
}
impl Entity {
#[must_use]
pub fn new(name: impl Into<Value>) -> Entity {
Entity {
name: name.into(),
predefined_type: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PartOf {
pub entity: Entity,
pub relation: Option<Relation>,
}
impl PartOf {
#[must_use]
pub fn new(entity: Entity) -> PartOf {
PartOf {
entity,
relation: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Relation {
Aggregates,
AssignsToGroup,
ContainedInSpatialStructure,
Nests,
VoidsElementFillsElement,
}
impl Relation {
pub const ALL: [Relation; 5] = [
Relation::Aggregates,
Relation::AssignsToGroup,
Relation::ContainedInSpatialStructure,
Relation::Nests,
Relation::VoidsElementFillsElement,
];
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Relation::Aggregates => "IFCRELAGGREGATES",
Relation::AssignsToGroup => "IFCRELASSIGNSTOGROUP",
Relation::ContainedInSpatialStructure => "IFCRELCONTAINEDINSPATIALSTRUCTURE",
Relation::Nests => "IFCRELNESTS",
Relation::VoidsElementFillsElement => "IFCRELVOIDSELEMENT IFCRELFILLSELEMENT",
}
}
#[must_use]
pub fn from_token(token: &str) -> Option<Relation> {
Relation::ALL.into_iter().find(|r| r.as_str() == token)
}
}
impl fmt::Display for Relation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Classification {
pub value: Option<Value>,
pub system: Value,
}
impl Classification {
#[must_use]
pub fn new(system: impl Into<Value>) -> Classification {
Classification {
value: None,
system: system.into(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute {
pub name: Value,
pub value: Option<Value>,
}
impl Attribute {
#[must_use]
pub fn new(name: impl Into<Value>) -> Attribute {
Attribute {
name: name.into(),
value: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Property {
pub property_set: Value,
pub base_name: Value,
pub value: Option<Value>,
pub data_type: Option<String>,
}
impl Property {
#[must_use]
pub fn new(property_set: impl Into<Value>, base_name: impl Into<Value>) -> Property {
Property {
property_set: property_set.into(),
base_name: base_name.into(),
value: None,
data_type: None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Material {
pub value: Option<Value>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Simple(String),
Restriction(Box<Restriction>),
}
impl From<&str> for Value {
fn from(value: &str) -> Value {
Value::Simple(value.to_owned())
}
}
impl From<String> for Value {
fn from(value: String) -> Value {
Value::Simple(value)
}
}
impl From<Restriction> for Value {
fn from(restriction: Restriction) -> Value {
Value::Restriction(Box::new(restriction))
}
}
impl Value {
#[must_use]
pub fn as_simple(&self) -> Option<&str> {
match self {
Value::Simple(value) => Some(value),
Value::Restriction(_) => None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Restriction {
pub base: String,
pub enumeration: Vec<String>,
pub patterns: Vec<String>,
pub min_inclusive: Option<String>,
pub max_inclusive: Option<String>,
pub min_exclusive: Option<String>,
pub max_exclusive: Option<String>,
pub length: Option<u64>,
pub min_length: Option<u64>,
pub max_length: Option<u64>,
pub total_digits: Option<u64>,
pub fraction_digits: Option<u64>,
}
impl Restriction {
#[must_use]
pub fn new(base: impl Into<String>) -> Restriction {
Restriction {
base: base.into(),
..Restriction::default()
}
}
#[must_use]
pub fn enumeration<I, S>(values: I) -> Restriction
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
Restriction {
enumeration: values.into_iter().map(Into::into).collect(),
..Restriction::new("string")
}
}
#[must_use]
pub fn pattern(pattern: impl Into<String>) -> Restriction {
Restriction {
patterns: vec![pattern.into()],
..Restriction::new("string")
}
}
}