blaise 0.1.4

A fast, local-first engine for GTFS transit data. Handles routing, fuzzy search, and geospatial queries without relying on external APIs.
Documentation
use chrono::{Local, Timelike};
use serde::{Deserialize, Serialize};
use std::{
    fmt::Display,
    ops::{Add, AddAssign, Sub, SubAssign},
};

pub const MIN: Time = Time(0);
pub const MAX: Time = Time(u32::MAX);

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Time(u32);

impl From<u32> for Time {
    fn from(value: u32) -> Self {
        Self(value)
    }
}

impl Sub<Time> for Time {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        Self(self.0 - rhs.0)
    }
}

impl Sub<Duration> for Time {
    type Output = Self;

    fn sub(self, rhs: Duration) -> Self::Output {
        Self(self.0 - rhs.0)
    }
}

impl Add<Time> for Time {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self(self.0 + rhs.0)
    }
}

impl AddAssign<Time> for Time {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0
    }
}

impl Add<Duration> for Time {
    type Output = Self;

    fn add(self, rhs: Duration) -> Self::Output {
        Self(self.0 + rhs.0)
    }
}

impl AddAssign<Duration> for Time {
    fn add_assign(&mut self, rhs: Duration) {
        self.0 += rhs.0
    }
}

impl Time {
    pub fn now() -> Self {
        let now = Local::now();
        Self(now.num_seconds_from_midnight())
    }

    pub const fn from_seconds(secs: u32) -> Self {
        Self(secs)
    }

    pub const fn as_seconds(&self) -> u32 {
        self.0
    }

    pub fn to_hms_string(&self) -> String {
        let h = self.0 / 3600;
        let m = (self.0 % 3600) / 60;
        let s = self.0 % 60;
        format!("{:02}:{:02}:{:02}", h, m, s)
    }

    pub fn from_hms(time: &str) -> Option<Self> {
        const HOUR_TO_SEC: u32 = 60 * 60;
        const MINUTE_TO_SEC: u32 = 60;
        let mut split = time.split(':');
        let hours: u32 = split.next()?.parse().ok()?;
        let hours = hours * HOUR_TO_SEC;
        let minutes: u32 = split.next()?.parse().ok()?;
        let minutes = minutes * MINUTE_TO_SEC;
        let seconds: u32 = split.next()?.parse().ok()?;
        let seconds = hours + minutes + seconds;
        Some(Self(seconds))
    }
}

#[test]
fn parse_unparse_1() {
    let time = "00:00:00";
    let stime = Time::from_hms(time).unwrap();
    assert_eq!(time, stime.to_hms_string())
}

#[test]
fn parse_unparse_2() {
    let time = "00:00:30";
    let stime = Time::from_hms(time).unwrap();
    assert_eq!(time, stime.to_hms_string())
}

#[test]
fn parse_unparse_3() {
    let time = "00:30:00";
    let stime = Time::from_hms(time).unwrap();
    assert_eq!(time, stime.to_hms_string())
}

#[test]
fn parse_unparse_4() {
    let time = "12:00:00";
    let stime = Time::from_hms(time).unwrap();
    assert_eq!(time, stime.to_hms_string())
}

#[test]
fn parse_unparse_5() {
    let time = "12:30:30";
    let stime = Time::from_hms(time).unwrap();
    assert_eq!(time, stime.to_hms_string())
}
#[test]
fn valid_time_test_1() {
    let time = "00:00:00";
    assert_eq!(Time::from_hms(time).unwrap().as_seconds(), 0);
}

#[test]
fn valid_time_test_2() {
    let time = "00:00:30";
    assert_eq!(Time::from_hms(time).unwrap().as_seconds(), 30);
}

#[test]
fn valid_time_test_3() {
    let time = "00:01:30";
    assert_eq!(Time::from_hms(time).unwrap().as_seconds(), 90);
}

#[test]
fn valid_time_test_4() {
    let time = "01:01:30";
    assert_eq!(Time::from_hms(time).unwrap().as_seconds(), 3690);
}

#[test]
fn invalid_time_test_1() {
    let time = "00:00:0a";
    assert!(Time::from_hms(time).is_none())
}
#[test]
fn invalid_time_test_2() {
    let time = "00:00";
    assert!(Time::from_hms(time).is_none())
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Duration(u32);

impl Duration {
    pub const fn from_seconds(secs: u32) -> Self {
        Self(secs)
    }

    pub const fn from_minutes(minutes: u32) -> Self {
        Self(minutes * 60)
    }

    pub const fn from_hours(hours: u32) -> Self {
        Self(hours * 60 * 60)
    }

    pub const fn from_days(days: u32) -> Self {
        Self(days * 60 * 60 * 24)
    }

    pub const fn as_seconds(&self) -> u32 {
        self.0
    }
}

impl From<u32> for Duration {
    fn from(value: u32) -> Self {
        Self(value)
    }
}

impl Display for Duration {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{}s", self.as_seconds()))
    }
}

impl Sub for Duration {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        Self(self.0 - rhs.0)
    }
}

impl SubAssign for Duration {
    fn sub_assign(&mut self, rhs: Self) {
        self.0 -= rhs.0
    }
}

impl Add for Duration {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self(self.0 + rhs.0)
    }
}

impl AddAssign for Duration {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0
    }
}