Skip to main content

vantage_sql/primitives/
interval.rs

1use vantage_expressions::{Expression, Expressive};
2
3/// Vendor-aware time interval literal.
4///
5/// Renders as:
6/// - **PostgreSQL:** `INTERVAL '1 year'`
7/// - **MySQL:**      `INTERVAL 1 YEAR`
8/// - **SQLite:**     a number of days: a week is 7, a month 30, a year 365,
9///   and hours round to whole days
10///
11/// # Examples
12///
13/// ```ignore
14/// use vantage_sql::primitives::interval::Interval;
15///
16/// Interval::new(1, "year")
17/// Interval::new(6, "month")
18/// Interval::new(30, "day")
19/// ```
20#[derive(Debug, Clone)]
21pub struct Interval {
22    amount: i64,
23    unit: String,
24}
25
26impl Interval {
27    /// `unit` is `day`, `week`, `month`, `year` or `hour`, singular or
28    /// plural. Use the singular when the query may run on MySQL.
29    pub fn new(amount: i64, unit: impl Into<String>) -> Self {
30        Self {
31            amount,
32            unit: unit.into().to_lowercase(),
33        }
34    }
35
36    #[cfg(feature = "sqlite")]
37    fn to_days(&self) -> i64 {
38        match self.unit.as_str() {
39            "year" | "years" => self.amount * 365,
40            "month" | "months" => self.amount * 30,
41            "week" | "weeks" => self.amount * 7,
42            "day" | "days" => self.amount,
43            "hour" | "hours" => (self.amount as f64 / 24.0).round() as i64,
44            _ => self.amount,
45        }
46    }
47}
48
49#[cfg(feature = "sqlite")]
50impl Expressive<crate::sqlite::types::AnySqliteType> for Interval {
51    fn expr(&self) -> Expression<crate::sqlite::types::AnySqliteType> {
52        Expression::new(format!("{}", self.to_days()), vec![])
53    }
54}
55
56#[cfg(feature = "mysql")]
57impl Expressive<crate::mysql::types::AnyMysqlType> for Interval {
58    fn expr(&self) -> Expression<crate::mysql::types::AnyMysqlType> {
59        Expression::new(
60            format!("INTERVAL {} {}", self.amount, self.unit.to_uppercase()),
61            vec![],
62        )
63    }
64}
65
66#[cfg(feature = "postgres")]
67impl Expressive<crate::postgres::types::AnyPostgresType> for Interval {
68    fn expr(&self) -> Expression<crate::postgres::types::AnyPostgresType> {
69        Expression::new(format!("INTERVAL '{} {}'", self.amount, self.unit), vec![])
70    }
71}