opening_hours/
context.rs

1use std::fmt::Debug;
2use std::sync::Arc;
3
4use compact_calendar::CompactCalendar;
5
6use crate::localization::{Localize, NoLocation};
7
8// --
9// -- Holidays
10// --
11
12/// Pairs a set of public holidays with a set of school holidays.
13#[derive(Clone, Default, Debug, Hash, PartialEq, Eq)]
14pub struct ContextHolidays {
15    pub(crate) public: Arc<CompactCalendar>,
16    pub(crate) school: Arc<CompactCalendar>,
17}
18
19impl ContextHolidays {
20    /// Create a new holidays context from sets of public and school holidays.
21    pub fn new(public: Arc<CompactCalendar>, school: Arc<CompactCalendar>) -> Self {
22        Self { public, school }
23    }
24
25    /// Get the set of public holidays attached to this context.
26    pub fn get_public(&self) -> &CompactCalendar {
27        &self.public
28    }
29
30    /// Get the set of school holidays attached to this context.
31    pub fn get_school(&self) -> &CompactCalendar {
32        &self.school
33    }
34}
35
36/// All the context attached to a parsed OpeningHours expression and that can
37/// alter its evaluation semantics.
38#[derive(Clone, Debug, Hash, PartialEq, Eq)]
39pub struct Context<L = NoLocation> {
40    /// A calendar use for evaluation of public and private holidays.
41    pub holidays: ContextHolidays,
42    /// Specify locality of the place attached to the expression: from
43    /// timezone to coordinates.
44    pub locale: L,
45    /// As an approximation, consider that any interval bigger that this size
46    /// is infinite. This can be enabled if you need better performance and
47    /// you don't care if a shop is open in more than a year.
48    pub approx_bound_interval_size: Option<chrono::TimeDelta>,
49}
50
51impl<L> Context<L> {
52    /// Attach a new holidays component to this context.
53    pub fn with_holidays(self, holidays: ContextHolidays) -> Self {
54        Self { holidays, ..self }
55    }
56
57    /// Attach a new locale component to this context.
58    pub fn with_locale<L2: Localize>(self, locale: L2) -> Context<L2> {
59        Context {
60            holidays: self.holidays,
61            locale,
62            approx_bound_interval_size: None,
63        }
64    }
65
66    /// Enables appromiation of long intervals.
67    pub fn approx_bound_interval_size(self, max_size: chrono::TimeDelta) -> Self {
68        Self { approx_bound_interval_size: Some(max_size), ..self }
69    }
70}
71
72#[cfg(feature = "auto-timezone")]
73impl Context<crate::localization::TzLocation<chrono_tz::Tz>> {
74    /// Create a context with given coordinates and try to infer a timezone and
75    /// a local holiday calendar.
76    ///
77    /// ```
78    /// use opening_hours::Context;
79    /// use opening_hours::localization::{Coordinates, Country, TzLocation};
80    ///
81    /// let coords = Coordinates::new(48.8535, 2.34839).unwrap();
82    ///
83    /// assert_eq!(
84    ///     Context::from_coords(coords),
85    ///     Context::default()
86    ///         .with_holidays(Country::FR.holidays())
87    ///         .with_locale(TzLocation::from_coords(coords)),
88    /// );
89    /// ```
90    #[cfg(feature = "auto-country")]
91    pub fn from_coords(coords: crate::localization::Coordinates) -> Self {
92        use crate::localization::Country;
93
94        let holidays = Country::try_from_coords(coords)
95            .map(Country::holidays)
96            .unwrap_or_default();
97
98        let locale = crate::localization::TzLocation::from_coords(coords);
99        Self { holidays, locale, approx_bound_interval_size: None }
100    }
101}
102
103impl Default for Context<NoLocation> {
104    fn default() -> Self {
105        Self {
106            holidays: Default::default(),
107            locale: NoLocation,
108            approx_bound_interval_size: None,
109        }
110    }
111}