use ifc_model::{Entity, EntityId, Model, Value};
pub mod slot {
pub const GLOBAL_ID: usize = 0;
pub const NAME: usize = 2;
pub const IDENTIFICATION: usize = 5;
pub const WORKING_TIMES: usize = 6;
pub const EXCEPTION_TIMES: usize = 7;
pub const PREDEFINED_TYPE: usize = 8;
}
pub mod work_time_slot {
pub const NAME: usize = 0;
pub const RECURRENCE_PATTERN: usize = 3;
pub const START: usize = 4;
pub const FINISH: usize = 5;
}
pub mod recurrence_slot {
pub const RECURRENCE_TYPE: usize = 0;
pub const DAY_COMPONENT: usize = 1;
pub const WEEKDAY_COMPONENT: usize = 2;
pub const MONTH_COMPONENT: usize = 3;
pub const POSITION: usize = 4;
pub const INTERVAL: usize = 5;
pub const OCCURRENCES: usize = 6;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorkTimeRole {
Working,
Exception,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RecurrenceType {
Daily,
Weekly,
MonthlyByDayOfMonth,
MonthlyByPosition,
ByDayCount,
ByWeekdayCount,
YearlyByDayOfMonth,
YearlyByPosition,
}
impl RecurrenceType {
fn parse(token: &str) -> Option<Self> {
Some(match token {
"DAILY" => Self::Daily,
"WEEKLY" => Self::Weekly,
"MONTHLY_BY_DAY_OF_MONTH" => Self::MonthlyByDayOfMonth,
"MONTHLY_BY_POSITION" => Self::MonthlyByPosition,
"BY_DAY_COUNT" => Self::ByDayCount,
"BY_WEEKDAY_COUNT" => Self::ByWeekdayCount,
"YEARLY_BY_DAY_OF_MONTH" => Self::YearlyByDayOfMonth,
"YEARLY_BY_POSITION" => Self::YearlyByPosition,
_ => return None,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Recurrence {
pub id: EntityId,
pub recurrence_type: Option<RecurrenceType>,
pub weekdays: Vec<i64>,
pub position: Option<i64>,
pub interval: Option<i64>,
pub occurrences: Option<i64>,
}
impl Recurrence {
#[must_use]
pub fn is_bounded(&self) -> bool {
self.occurrences.is_some()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WorkTime {
pub id: EntityId,
pub role: WorkTimeRole,
pub name: Option<String>,
pub start: Option<String>,
pub finish: Option<String>,
pub recurrence: Option<Recurrence>,
}
#[derive(Debug, Clone, Copy)]
pub struct WorkCalendar<'m> {
id: EntityId,
entity: &'m Entity,
}
impl<'m> WorkCalendar<'m> {
#[must_use]
pub fn id(&self) -> EntityId {
self.id
}
#[must_use]
pub fn name(&self) -> Option<&'m str> {
self.entity.text(slot::NAME)
}
#[must_use]
pub fn identification(&self) -> Option<&'m str> {
self.entity.text(slot::IDENTIFICATION)
}
#[must_use]
pub fn predefined_type(&self) -> Option<&'m str> {
match self.entity.attribute(slot::PREDEFINED_TYPE)? {
Value::Enum(token) => Some(token),
_ => None,
}
}
#[must_use]
pub fn working_times(&self, model: &Model) -> Vec<WorkTime> {
self.times(model, slot::WORKING_TIMES, WorkTimeRole::Working)
}
#[must_use]
pub fn exception_times(&self, model: &Model) -> Vec<WorkTime> {
self.times(model, slot::EXCEPTION_TIMES, WorkTimeRole::Exception)
}
fn times(&self, model: &Model, slot: usize, role: WorkTimeRole) -> Vec<WorkTime> {
let mut refs = Vec::new();
if let Some(v) = self.entity.attribute(slot) {
v.for_each_ref(&mut |id| refs.push(id));
}
refs.into_iter()
.filter_map(|id| read_work_time(model, id, role))
.collect()
}
}
fn read_work_time(model: &Model, id: EntityId, role: WorkTimeRole) -> Option<WorkTime> {
let entity = model.get(id)?;
if !entity.type_name.eq_ignore_ascii_case("IFCWORKTIME") {
return None;
}
let recurrence = match entity.attribute(work_time_slot::RECURRENCE_PATTERN) {
Some(Value::Ref(pattern)) => read_recurrence(model, *pattern),
_ => None,
};
Some(WorkTime {
id,
role,
name: entity.text(work_time_slot::NAME).map(str::to_string),
start: entity.text(work_time_slot::START).map(str::to_string),
finish: entity.text(work_time_slot::FINISH).map(str::to_string),
recurrence,
})
}
fn read_recurrence(model: &Model, id: EntityId) -> Option<Recurrence> {
let entity = model.get(id)?;
if !entity
.type_name
.eq_ignore_ascii_case("IFCRECURRENCEPATTERN")
{
return None;
}
let recurrence_type = match entity.attribute(recurrence_slot::RECURRENCE_TYPE) {
Some(Value::Enum(token)) => RecurrenceType::parse(token),
_ => None,
};
let mut weekdays = Vec::new();
if let Some(Value::List(items)) = entity.attribute(recurrence_slot::WEEKDAY_COMPONENT) {
for item in items {
if let Some(day) = item.unwrap_typed().as_i64() {
weekdays.push(day);
}
}
}
Some(Recurrence {
id,
recurrence_type,
weekdays,
position: entity
.attribute(recurrence_slot::POSITION)
.and_then(|v| v.unwrap_typed().as_i64()),
interval: entity
.attribute(recurrence_slot::INTERVAL)
.and_then(|v| v.unwrap_typed().as_i64()),
occurrences: entity
.attribute(recurrence_slot::OCCURRENCES)
.and_then(|v| v.unwrap_typed().as_i64()),
})
}
#[must_use]
pub fn work_calendars(model: &Model) -> Vec<WorkCalendar<'_>> {
model
.of_type("IFCWORKCALENDAR")
.map(|(id, entity)| WorkCalendar { id, entity })
.collect()
}