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 }