File indexing completed on 2024-12-01 04:19:21

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KOPENINGHOURS_SELECTORS_P_H
0008 #define KOPENINGHOURS_SELECTORS_P_H
0009 
0010 #include "interval.h"
0011 
0012 #include <memory>
0013 
0014 namespace KOpeningHours {
0015 
0016 class OpeningHoursPrivate;
0017 
0018 namespace Capability {
0019     enum RequiredCapabilities {
0020         None = 0,
0021         PublicHoliday = 1,
0022         SchoolHoliday = 2,
0023         Location = 4,
0024         NotImplemented = 8,
0025         Interval = 16,
0026         PointInTime = 32,
0027     };
0028 }
0029 
0030 /** Result from selector evaluation. */
0031 class SelectorResult {
0032 public:
0033     /** Selector will never match. */
0034     inline SelectorResult(bool = false) : m_matching(false) {}
0035     /** Selector will match in @p offset seconds. */
0036     inline SelectorResult(qint64 offset)
0037         : m_offset(offset)
0038         , m_matching(offset >= 0)
0039         {}
0040     /** Selector matches for @p interval. */
0041     inline SelectorResult(const Interval &interval) : m_interval(interval) {}
0042 
0043     inline bool operator<(const SelectorResult &other) const {
0044         if (m_matching == other.m_matching) {
0045             if (m_offset == other.m_offset) {
0046                 return m_interval < other.m_interval;
0047             }
0048             return m_offset < other.m_offset;
0049         }
0050         return m_matching && !other.m_matching;
0051     }
0052 
0053     inline bool canMatch() const { return m_matching; }
0054     inline int64_t matchOffset() const { return m_offset; }
0055     inline Interval interval() const { return m_interval; }
0056 
0057 private:
0058     Interval m_interval;
0059     int64_t m_offset = 0;
0060     bool m_matching = true;
0061 };
0062 
0063 // see https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification, the below names/types follow that
0064 
0065 template <typename T>
0066 void appendSelector(T* firstSelector, std::unique_ptr<T> &&selector)
0067 {
0068     while(firstSelector->next) {
0069         firstSelector = firstSelector->next.get();
0070     }
0071     firstSelector->next = std::move(selector);
0072 }
0073 
0074 template <typename T>
0075 void appendSelector(T* firstSelector, T* selector)
0076 {
0077     return appendSelector(firstSelector, std::unique_ptr<T>(selector));
0078 }
0079 
0080 template <typename T>
0081 T* lastSelector(T* firstSelector)
0082 {
0083     while (firstSelector && firstSelector->next) {
0084         firstSelector = firstSelector->next.get();
0085     }
0086     return firstSelector;
0087 }
0088 
0089 /** Time */
0090 class Time
0091 {
0092 public:
0093     static bool isValid(Time t);
0094     static void convertFromAm(Time &t);
0095     static void convertFromPm(Time &t);
0096     static Time parse(const char *begin, const char *end);
0097     QByteArray toExpression(bool end) const;
0098 
0099     enum Event {
0100         NoEvent,
0101         Dawn,
0102         Sunrise,
0103         Sunset,
0104         Dusk,
0105     };
0106     Event event;
0107     int hour;
0108     int minute;
0109 
0110 };
0111 
0112 inline constexpr bool operator==(Time lhs, Time rhs)
0113 {
0114     return lhs.event == rhs.event
0115         && lhs.hour == rhs.hour
0116         && lhs.minute == rhs.minute;
0117 }
0118 
0119 /** Time span selector. */
0120 class Timespan
0121 {
0122 public:
0123     int requiredCapabilities() const;
0124     bool isMultiDay(QDate date, OpeningHoursPrivate *context) const;
0125     SelectorResult nextInterval(const Interval &interval, const QDateTime &dt, OpeningHoursPrivate *context) const;
0126     QByteArray toExpression() const;
0127     Time adjustedEnd() const;
0128     bool operator==(Timespan &other) const;
0129 
0130     Time begin = { Time::NoEvent, -1, -1 };
0131     Time end = { Time::NoEvent, -1, -1 };
0132     int interval = 0;
0133     bool openEnd = false;
0134     bool pointInTime = false;
0135     std::unique_ptr<Timespan> next;
0136 };
0137 
0138 struct NthEntry {
0139     int begin;
0140     int end;
0141     QByteArray toExpression() const;
0142 };
0143 
0144 /** Nth week days, like 1-2,4,6-8 */
0145 class NthSequence
0146 {
0147 public:
0148     void add(NthEntry range);
0149     QByteArray toExpression() const;
0150     std::vector<NthEntry> sequence;
0151 };
0152 
0153 /** Weekday range. */
0154 class WeekdayRange
0155 {
0156 public:
0157     int requiredCapabilities() const;
0158     SelectorResult nextInterval(const Interval &interval, const QDateTime &dt, OpeningHoursPrivate *context) const;
0159     SelectorResult nextIntervalLocal(const Interval &interval, const QDateTime &dt, OpeningHoursPrivate *context) const;
0160     QByteArray toExpression() const;
0161     void simplify();
0162 
0163     uint8_t beginDay = 0; // Mo=1, Tu=2, ..., Su=7
0164     uint8_t endDay = 0;
0165     std::unique_ptr<NthSequence> nthSequence;
0166     int16_t offset = 0;
0167     enum Holiday : uint8_t {
0168         NoHoliday = 0,
0169         PublicHoliday = 1,
0170         SchoolHoliday = 2,
0171     };
0172     Holiday holiday = NoHoliday;
0173     std::unique_ptr<WeekdayRange> next;
0174     std::unique_ptr<WeekdayRange> lhsAndSelector;
0175     std::unique_ptr<WeekdayRange> rhsAndSelector;
0176 };
0177 
0178 /** Week */
0179 class Week
0180 {
0181 public:
0182     int requiredCapabilities() const;
0183     SelectorResult nextInterval(const Interval &interval, const QDateTime &dt, OpeningHoursPrivate *context) const;
0184     QByteArray toExpression() const;
0185 
0186     uint8_t beginWeek = 0;
0187     uint8_t endWeek = 0;
0188     uint8_t interval = 1;
0189     std::unique_ptr<Week> next;
0190 };
0191 
0192 /** Day or weekday-based offset to a Date. */
0193 class DateOffset
0194 {
0195 public:
0196     bool operator==(DateOffset other) const;
0197     DateOffset &operator+=(DateOffset other);
0198 
0199     int16_t dayOffset;
0200     int8_t weekday;
0201     int8_t nthWeekday;
0202 };
0203 
0204 class MonthdayRange;
0205 
0206 /** Date */
0207 class Date
0208 {
0209 public:
0210     QByteArray toExpression(const Date &refDate, const MonthdayRange &prev) const;
0211     bool operator==(Date other) const;
0212     bool operator!=(Date other) const { return !operator==(other); }
0213     bool hasOffset() const;
0214 
0215     int year;
0216     int month;
0217     int day;
0218     enum VariableDate : uint8_t {
0219         FixedDate,
0220         Easter
0221     };
0222     VariableDate variableDate;
0223     DateOffset offset;
0224 };
0225 
0226 /** Monthday range. */
0227 class MonthdayRange
0228 {
0229 public:
0230     int requiredCapabilities() const;
0231     SelectorResult nextInterval(const Interval &interval, const QDateTime &dt, OpeningHoursPrivate *context) const;
0232     QByteArray toExpression(const MonthdayRange &prev) const;
0233     void simplify();
0234 
0235     Date begin = { 0, 0, 0, Date::FixedDate, { 0, 0, 0 } };
0236     Date end = { 0, 0, 0, Date::FixedDate, { 0, 0, 0 } };
0237     std::unique_ptr<MonthdayRange> next;
0238 };
0239 
0240 /** Year range. */
0241 class YearRange
0242 {
0243 public:
0244     int requiredCapabilities() const;
0245     SelectorResult nextInterval(const Interval &interval, const QDateTime &dt, OpeningHoursPrivate *context) const;
0246     QByteArray toExpression() const;
0247 
0248     int begin = 0;
0249     int end = 0;
0250     int interval = 1;
0251     std::unique_ptr<YearRange> next;
0252 };
0253 }
0254 
0255 #endif // KOPENINGHOURS_SELECTORS_P_H