taler-rust

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

utils.rs (4546B)


      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 /// Fixed sized inlined ASCII string
     27 pub struct InlineAscii<const LEN: usize> {
     28     /// Buffer of ASCII bytes
     29     // TODO use std::ascii::Char when stable
     30     buf: [u8; LEN],
     31 }
     32 
     33 impl<const LEN: usize> From<[u8; LEN]> for InlineAscii<LEN> {
     34     fn from(buf: [u8; LEN]) -> Self {
     35         assert!(buf.is_ascii());
     36         Self { buf }
     37     }
     38 }
     39 
     40 impl<const LEN: usize> AsRef<str> for InlineAscii<LEN> {
     41     #[inline]
     42     fn as_ref(&self) -> &str {
     43         // SAFETY: buf are all ASCII
     44         unsafe { std::str::from_utf8_unchecked(&self.buf) }
     45     }
     46 }
     47 
     48 impl<const LEN: usize> std::fmt::Display for InlineAscii<LEN> {
     49     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     50         f.write_str(self.as_ref())
     51     }
     52 }
     53 
     54 impl<const LEN: usize> std::fmt::Debug for InlineAscii<LEN> {
     55     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     56         f.write_str(self.as_ref())
     57     }
     58 }
     59 
     60 #[derive(Clone, Copy, PartialEq, Eq)]
     61 /// Inlined ASCII string
     62 pub struct InlineStr<const MAX: usize> {
     63     /// Len of ascii string in buf
     64     len: u8,
     65     /// Buffer of ASCII bytes
     66     // TODO use std::ascii::Char when stable
     67     buf: [u8; MAX],
     68 }
     69 
     70 impl<const MAX: usize> InlineStr<MAX> {
     71     /// Create an inlined string from a slice
     72     #[inline]
     73     pub const fn copy_from_slice(slice: &[u8]) -> Self {
     74         assert!(slice.is_ascii());
     75         let len = slice.len();
     76         let mut buf = [0; MAX];
     77         // TODO use buf[..len].copy_from_slice(slice); once allowed in const
     78         let mut i = 0;
     79         while i < len {
     80             buf[i] = slice[i];
     81             i += 1;
     82         }
     83         Self {
     84             len: len as u8,
     85             buf,
     86         }
     87     }
     88 
     89     /// Create an inlined string from a string
     90     /// Return none if too long
     91     #[inline]
     92     pub fn try_from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Option<Self> {
     93         let mut len = 0;
     94         let mut buf = [0; MAX];
     95         for byte in iter {
     96             *buf.get_mut(len)? = byte;
     97             len += 1;
     98         }
     99         assert!(buf.is_ascii());
    100         Some(Self {
    101             len: len as u8,
    102             buf,
    103         })
    104     }
    105 
    106     /// # Safety
    107     /// You must only write valid ASCII chars
    108     #[inline]
    109     pub unsafe fn deref_mut(&mut self) -> &mut [u8] {
    110         // SAFETY: len <= MAX
    111         unsafe { self.buf.get_unchecked_mut(..self.len as usize) }
    112     }
    113 }
    114 
    115 impl<const MAX: usize> AsRef<str> for InlineStr<MAX> {
    116     #[inline]
    117     fn as_ref(&self) -> &str {
    118         // SAFETY: len <= MAX && buf[..len] are all ASCII
    119         unsafe { std::str::from_utf8_unchecked(self.buf.get_unchecked(..self.len as usize)) }
    120     }
    121 }
    122 
    123 impl<const MAX: usize> Deref for InlineStr<MAX> {
    124     type Target = [u8];
    125 
    126     #[inline]
    127     fn deref(&self) -> &Self::Target {
    128         // SAFETY: len <= MAX
    129         unsafe { self.buf.get_unchecked(..self.len as usize) }
    130     }
    131 }
    132 
    133 impl<const MAX: usize> std::fmt::Display for InlineStr<MAX> {
    134     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    135         f.write_str(self.as_ref())
    136     }
    137 }
    138 
    139 impl<const MAX: usize> std::fmt::Debug for InlineStr<MAX> {
    140     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    141         f.write_str(self.as_ref())
    142     }
    143 }
    144 
    145 /** Convert a date to a UTC timestamp */
    146 pub fn date_to_utc_ts(date: &Date) -> Timestamp {
    147     date.to_zoned(TimeZone::UTC).unwrap().timestamp()
    148 }
    149 
    150 /** Convert a date time to a UTC timestamp */
    151 pub fn date_time_to_utc_ts(date: &DateTime) -> Timestamp {
    152     date.to_zoned(TimeZone::UTC).unwrap().timestamp()
    153 }
    154 
    155 /** Get current timestamp truncated to micros precision */
    156 pub fn now_sql_stable_ts() -> Timestamp {
    157     Timestamp::from_microsecond(Timestamp::now().as_microsecond()).unwrap()
    158 }