Skip to main content

tibba_model/
lib.rs

1// Copyright 2026 Tree xie.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use chrono::{DateTime, offset};
16use snafu::Snafu;
17use tibba_error::Error as BaseError;
18use time::macros::format_description;
19use time::{OffsetDateTime, PrimitiveDateTime};
20
21pub fn format_datetime(datetime: PrimitiveDateTime) -> String {
22    let ts = datetime.assume_utc().unix_timestamp();
23    if let Some(value) = DateTime::from_timestamp(ts, 0) {
24        value.with_timezone(&offset::Local).to_string()
25    } else {
26        String::new()
27    }
28}
29
30pub fn parse_primitive_datetime(s: &str) -> Result<PrimitiveDateTime> {
31    let fmt_t = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
32    let fmt_space = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
33    if let Ok(dt) = PrimitiveDateTime::parse(s, fmt_t) {
34        return Ok(dt);
35    }
36    if let Ok(dt) = PrimitiveDateTime::parse(s, fmt_space) {
37        return Ok(dt);
38    }
39    if let Ok(odt) = OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339) {
40        let utc = odt.to_offset(time::UtcOffset::UTC);
41        return Ok(PrimitiveDateTime::new(utc.date(), utc.time()));
42    }
43    Err(Error::InvalidDatetime {
44        value: s.to_string(),
45    })
46}
47
48type Result<T> = std::result::Result<T, Error>;
49
50#[derive(Debug, Snafu)]
51#[snafu(visibility(pub))]
52pub enum Error {
53    #[snafu(display("{source}"))]
54    Sqlx { source: sqlx::Error },
55    #[snafu(display("{source}"))]
56    Json { source: serde_json::Error },
57    #[snafu(display("Not supported function: {}", name))]
58    NotSupported { name: String },
59    #[snafu(display("Not found"))]
60    NotFound,
61    #[snafu(display("Invalid datetime: {value}"))]
62    InvalidDatetime { value: String },
63    #[snafu(display("Insufficient balance"))]
64    InsufficientBalance,
65    #[snafu(display("{message}"))]
66    InvalidAmount { message: String },
67}
68
69impl From<Error> for BaseError {
70    fn from(val: Error) -> Self {
71        let err = match val {
72            Error::Sqlx { source } => BaseError::new(source)
73                .with_sub_category("sqlx")
74                .with_exception(true),
75            Error::Json { source } => BaseError::new(source)
76                .with_sub_category("json")
77                .with_exception(true),
78            Error::NotSupported { name } => {
79                BaseError::new(format!("Not supported function: {name}"))
80                    .with_sub_category("not_supported")
81                    .with_exception(true)
82            }
83            Error::NotFound => BaseError::new("Not found")
84                .with_sub_category("not_found")
85                .with_exception(true),
86            Error::InvalidDatetime { value } => {
87                BaseError::new(format!("Invalid datetime: {value}"))
88                    .with_sub_category("invalid_datetime")
89            }
90            Error::InsufficientBalance => BaseError::new("Insufficient balance")
91                .with_sub_category("insufficient_balance")
92                .with_status(402),
93            Error::InvalidAmount { message } => BaseError::new(message)
94                .with_sub_category("invalid_amount")
95                .with_status(400),
96        };
97        err.with_category("model")
98    }
99}
100
101mod configuration;
102mod model;
103mod schema;
104mod user;
105
106pub use configuration::*;
107pub use model::*;
108pub use schema::*;
109pub use user::*;