taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

utils.rs (3626B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2025, 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::ops::Deref;
     18 
     19 use jiff::{
     20     Timestamp,
     21     civil::{Date, DateTime},
     22     tz::TimeZone,
     23 };
     24 
     25 #[derive(Clone, Copy, PartialEq, Eq)]
     26 pub struct InlineStr<const LEN: usize> {
     27     /// Len of ascii string in buf
     28     len: u8,
     29     /// Buffer of ascii bytes, left adjusted and zero padded
     30     // TODO use std::ascii::Char when stable
     31     buf: [u8; LEN],
     32 }
     33 
     34 impl<const LEN: usize> InlineStr<LEN> {
     35     /// Create an inlined string from a slice
     36     #[inline]
     37     pub const fn copy_from_slice(slice: &[u8]) -> Self {
     38         let len = slice.len();
     39         let mut buf = [0; LEN];
     40         // TODO use buf[..len].copy_from_slice(slice); once allowed in const
     41         let mut i = 0;
     42         while i < len {
     43             buf[i] = slice[i];
     44             i += 1;
     45         }
     46 
     47         debug_assert!(buf.is_ascii());
     48         Self {
     49             len: len as u8,
     50             buf,
     51         }
     52     }
     53 
     54     /// Create an inlined string from a string
     55     /// Return none if too long
     56     #[inline]
     57     pub fn try_from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Option<Self> {
     58         let mut len = 0;
     59         let mut buf = [0; LEN];
     60         for byte in iter {
     61             *buf.get_mut(len)? = byte;
     62             len += 1;
     63         }
     64         debug_assert!(buf.is_ascii());
     65         Some(Self {
     66             len: len as u8,
     67             buf,
     68         })
     69     }
     70 
     71     /// # Safety
     72     /// You must only write valid ASCII chars
     73     #[inline]
     74     pub unsafe fn deref_mut(&mut self) -> &mut [u8] {
     75         // SAFETY: len <= LEN
     76         unsafe { self.buf.get_unchecked_mut(..self.len as usize) }
     77     }
     78 }
     79 
     80 impl<const LEN: usize> AsRef<str> for InlineStr<LEN> {
     81     #[inline]
     82     fn as_ref(&self) -> &str {
     83         // SAFETY: len <= LEN && buf[..len] are all ASCII uppercase
     84         unsafe { std::str::from_utf8_unchecked(self.buf.get_unchecked(..self.len as usize)) }
     85     }
     86 }
     87 
     88 impl<const LEN: usize> Deref for InlineStr<LEN> {
     89     type Target = [u8];
     90 
     91     #[inline]
     92     fn deref(&self) -> &Self::Target {
     93         // SAFETY: len <= LEN
     94         unsafe { self.buf.get_unchecked(..self.len as usize) }
     95     }
     96 }
     97 
     98 impl<const LEN: usize> std::fmt::Display for InlineStr<LEN> {
     99     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    100         f.write_str(self.as_ref())
    101     }
    102 }
    103 
    104 impl<const LEN: usize> std::fmt::Debug for InlineStr<LEN> {
    105     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    106         f.write_str(self.as_ref())
    107     }
    108 }
    109 
    110 /** Convert a date to a UTC timestamp */
    111 pub fn date_to_utc_ts(date: &Date) -> Timestamp {
    112     date.to_zoned(TimeZone::UTC).unwrap().timestamp()
    113 }
    114 
    115 /** Convert a date time to a UTC timestamp */
    116 pub fn date_time_to_utc_ts(date: &DateTime) -> Timestamp {
    117     date.to_zoned(TimeZone::UTC).unwrap().timestamp()
    118 }
    119 
    120 /** Get current timestamp truncated to micros precision */
    121 pub fn now_sql_stable_ts() -> Timestamp {
    122     Timestamp::from_microsecond(Timestamp::now().as_microsecond()).unwrap()
    123 }