Skip to content

Commit

Permalink
make comments API more sound
Browse files Browse the repository at this point in the history
  • Loading branch information
remi-dupre committed Feb 27, 2025
1 parent 74374cf commit 5ed4542
Show file tree
Hide file tree
Showing 36 changed files with 496 additions and 434 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run: poetry run maturin develop

- name: Build python documentation
run: poetry run pdoc -o docs -d numpy opening_hours
run: poetry run pdoc --no-show-source -o docs -d numpy opening_hours

- name: Publish to github pages
uses: peaceiris/actions-gh-pages@v3
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 2.0.0

### General

- **(breaking)** Intervals from iterators will now return a unique comment (an
empty string if there is no comment).
- **(breaking)** `next_change()` can now return a date where the facility remains
open or remains closed if the comment changes.
- **(breaking)** `state()` now returns the current comment together with the
rule kind.

### Python

- **(breaking)** `State` has been renamed to `RuleKind`.

## 1.1.1

### Rust
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opening-hours"
version = "1.1.1"
version = "2.0.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down Expand Up @@ -29,9 +29,9 @@ log = ["opening-hours-syntax/log", "dep:log"]

[dependencies]
chrono = "0.4"
compact-calendar = { path = "compact-calendar", version = "1.1.1" }
compact-calendar = { path = "compact-calendar", version = "2.0.0" }
flate2 = "1.0"
opening-hours-syntax = { path = "opening-hours-syntax", version = "1.1.1" }
opening-hours-syntax = { path = "opening-hours-syntax", version = "2.0.0" }
sunrise-next = "1.2"

# Feature: log (default)
Expand All @@ -46,7 +46,7 @@ tzf-rs = { version = "0.4", default-features = false, optional = true }

[build-dependencies]
chrono = "0.4"
compact-calendar = { path = "compact-calendar", version = "1.1.1" }
compact-calendar = { path = "compact-calendar", version = "2.0.0" }
country-boundaries = { version = "1.2", optional = true }
flate2 = "1.0"
rustc_version = "0.4.0"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Add this to your `Cargo.toml`:

```toml
[dependencies]
opening-hours = "0"
opening-hours = "2"
```

Here's a simple example that parse an opening hours description and displays
Expand Down
2 changes: 1 addition & 1 deletion compact-calendar/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "compact-calendar"
version = "1.1.1"
version = "2.0.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down
5 changes: 2 additions & 3 deletions fuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use opening_hours::localization::{Coordinates, Localize};
use opening_hours::{Context, OpeningHours};

const MAX_INTERVAL_RANGE: chrono::TimeDelta = chrono::TimeDelta::days(366 * 10);
const MAX_OH_LENGTH: usize = 256;

/// A fuzzing example
#[derive(Arbitrary, Clone)]
Expand Down Expand Up @@ -69,8 +70,7 @@ impl Debug for Data {
/// Run a fuzzing test and return `true` if the example should be kept in
/// corpus.
pub fn run_fuzz_oh(data: Data) -> bool {
if data.oh.contains('=') {
// The fuzzer spends way too much time building comments.
if data.oh.len() > MAX_OH_LENGTH {
return false;
}

Expand Down Expand Up @@ -110,7 +110,6 @@ pub fn run_fuzz_oh(data: Data) -> bool {
let date = ctx.locale.datetime(date);
let oh_1 = oh_1.with_context(ctx.clone());
let oh_2 = oh_2.with_context(ctx.clone());

assert_eq!(oh_1.state(date), oh_2.state(date));
assert_eq!(oh_1.next_change(date), oh_2.next_change(date));
} else {
Expand Down
6 changes: 3 additions & 3 deletions opening-hours-py/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opening-hours-py"
version = "1.1.1"
version = "2.0.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand All @@ -23,12 +23,12 @@ pyo3-stub-gen = "0.7"
[dependencies.opening-hours-rs]
package = "opening-hours"
path = ".."
version = "1.1.1"
version = "2.0.0"
features = ["log", "auto-country", "auto-timezone"]

[dependencies.opening-hours-syntax]
path = "../opening-hours-syntax"
version = "1.1.1"
version = "2.0.0"
features = ["log"]

[dependencies.pyo3]
Expand Down
15 changes: 8 additions & 7 deletions opening-hours-py/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ impl PyOpeningHours {
PyOpeningHours { inner: self.inner.normalize() }
}

/// Get current state of the time domain, the state can be either "open",
/// "closed" or "unknown".
/// Get current state of the time domain together with current comment. The state can be either
/// "open", "closed" or "unknown".
///
/// Parameters
/// ----------
Expand All @@ -178,11 +178,12 @@ impl PyOpeningHours {
/// Examples
/// --------
/// >>> OpeningHours("24/7 off").state()
/// State.CLOSED
/// (State.CLOSED, '')
#[pyo3(signature = (time=None))]
fn state(&self, time: Option<DateTimeMaybeAware>) -> State {
fn state(&self, time: Option<DateTimeMaybeAware>) -> (State, String) {
let time = DateTimeMaybeAware::unwrap_or_now(time);
self.inner.state(time).into()
let (kind, comment) = self.inner.state(time);
(kind.into(), comment.to_string())
}

/// Check if current state is open.
Expand Down Expand Up @@ -267,9 +268,9 @@ impl PyOpeningHours {
/// --------
/// >>> intervals = OpeningHours("2099Mo-Su 12:30-17:00").intervals()
/// >>> next(intervals)
/// (..., datetime.datetime(2099, 1, 1, 12, 30), State.CLOSED, [])
/// (..., datetime.datetime(2099, 1, 1, 12, 30), State.CLOSED, '')
/// >>> next(intervals)
/// (datetime.datetime(2099, 1, 1, 12, 30), datetime.datetime(2099, 1, 1, 17, 0), State.OPEN, [])
/// (datetime.datetime(2099, 1, 1, 12, 30), datetime.datetime(2099, 1, 1, 17, 0), State.OPEN, '')
#[pyo3(signature = (start=None, end=None))]
fn intervals(
&self,
Expand Down
4 changes: 2 additions & 2 deletions opening-hours-py/src/types/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl RangeIterator {
DateTimeMaybeAware,
Option<DateTimeMaybeAware>,
State,
Vec<String>,
String,
)> {
let dt_range = slf.iter.next()?;

Expand All @@ -66,7 +66,7 @@ impl RangeIterator {
slf.map_prefered_timezone(dt_range.range.end)
.map_date_limit(),
dt_range.kind.into(),
dt_range.comments.iter().map(|c| c.to_string()).collect(),
dt_range.comment.to_string(),
))
}
}
Expand Down
2 changes: 1 addition & 1 deletion opening-hours-syntax/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opening-hours-syntax"
version = "1.1.1"
version = "2.0.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down
9 changes: 9 additions & 0 deletions opening-hours-syntax/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt;

use crate::parser::Rule;
use crate::rules::day::{Year, YearRange};

pub type Result<T> = std::result::Result<T, Error>;

Expand All @@ -10,6 +11,7 @@ pub enum Error {
Unsupported(&'static str),
Overflow { value: String, expected: String },
InvalidExtendTime { hour: u8, minutes: u8 },
InvertedYearRange { start: Year, end: Year, step: u16 },
}

impl From<pest::error::Error<Rule>> for Error {
Expand All @@ -30,6 +32,13 @@ impl fmt::Display for Error {
Self::Overflow { value, expected } => {
write!(f, "{value} is too large: expected {expected}")
}
&Self::InvertedYearRange { start, end, step } => {
write!(
f,
"Inverted year ranges are ambiguous, do you mean '{}'?",
YearRange::new(end..=start, step).unwrap(),
)
}
}
}
}
Expand Down
1 change: 0 additions & 1 deletion opening-hours-syntax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ mod parser;
pub mod error;
pub mod extended_time;
pub mod rules;
pub mod sorted_vec;

pub use error::{Error, Result};
pub use extended_time::ExtendedTime;
Expand Down
14 changes: 6 additions & 8 deletions opening-hours-syntax/src/normalize/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use chrono::Weekday;
use crate::normalize::paving::{Paving5D, Selector5D};
use crate::rules::day::{Month, MonthdayRange, WeekDayRange, WeekNum, WeekRange, Year, YearRange};
use crate::rules::time::{Time, TimeSpan};
use crate::sorted_vec::UniqueSortedVec;
use crate::{ExtendedTime, RuleKind};

use super::frame::{Bounded, Frame};
Expand All @@ -18,7 +17,7 @@ pub(crate) type Canonical = Paving5D<
Frame<Month>,
Frame<WeekNum>,
Frame<OrderedWeekday>,
(RuleKind, UniqueSortedVec<Arc<str>>),
(RuleKind, Arc<str>),
>;

pub(crate) type CanonicalSelector =
Expand Down Expand Up @@ -99,18 +98,17 @@ impl MakeCanonical for YearRange {
type CanonicalType = Frame<Year>;

fn try_make_canonical(&self) -> Option<Range<Self::CanonicalType>> {
if self.step != 1 {
let (range, step) = self.into_parts();

if step != 1 {
return None;
}

Some(Frame::to_range_strict(self.range.clone()))
Some(Frame::to_range_strict(range))
}

fn into_type(canonical: Range<Self::CanonicalType>) -> Option<Self> {
Some(YearRange {
range: Frame::to_range_inclusive(canonical)?,
step: 1,
})
YearRange::new(Frame::to_range_inclusive(canonical)?, 1)
}
}

Expand Down
9 changes: 4 additions & 5 deletions opening-hours-syntax/src/normalize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,11 @@ pub(crate) fn canonical_to_seq(mut canonical: Canonical) -> impl Iterator<Item =

std::iter::from_fn(move || {
// Extract open periods first, then unknowns
let ((kind, comments), selector) = [RuleKind::Open, RuleKind::Unknown, RuleKind::Closed]
let ((kind, comment), selector) = [RuleKind::Open, RuleKind::Unknown, RuleKind::Closed]
.into_iter()
.find_map(|target_kind| {
canonical.pop_filter(|(kind, comments)| {
*kind == target_kind
&& (target_kind != RuleKind::Closed || !comments.is_empty())
canonical.pop_filter(|(kind, comment)| {
*kind == target_kind && (target_kind != RuleKind::Closed || !comment.is_empty())
})
})?;

Expand Down Expand Up @@ -77,7 +76,7 @@ pub(crate) fn canonical_to_seq(mut canonical: Canonical) -> impl Iterator<Item =
time_selector,
kind,
operator,
comments,
comment,
})
})
}
Loading

0 comments on commit 5ed4542

Please sign in to comment.