pub mod connections;
pub mod datetime;
pub mod fnmatch;
pub mod futures;
pub mod random;
pub mod vobject;
#[macro_use]
pub mod logging;
pub mod lock;
pub mod parsec;
pub mod percent_encoding;
pub mod shellexpand;
#[cfg(feature = "sqlite3")]
pub mod sqlite3;
#[cfg(test)]
mod tests;
pub mod urn;
pub mod xdg;
pub fn base36(mut m: u64) -> String {
if m == 0 {
return "0".to_string();
}
let mut ret = String::new();
while m >= 36 {
ret.push(char::from_digit((m % 36) as u32, 36).unwrap());
m /= 36;
}
if m != 0 {
ret.push(char::from_digit(m as u32, 36).unwrap());
}
ret.chars().rev().collect()
}
pub mod html_escape {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum HtmlEntity {
Lt,
Gt,
Amp,
Quot,
}
impl HtmlEntity {
pub const ALL: [&'static str; 4] = ["<", ">", "&", """];
pub const GLYPHS: [&'static str; 4] = ["<", ">", "&", "\""];
pub const fn glyph(self) -> char {
match self {
Self::Lt => '<',
Self::Gt => '>',
Self::Amp => '&',
Self::Quot => '"',
}
}
pub const fn name(self) -> &'static str {
match self {
Self::Lt => "lt",
Self::Gt => "gt",
Self::Amp => "amp",
Self::Quot => "quot",
}
}
pub const fn syntax(self) -> &'static str {
match self {
Self::Lt => "<",
Self::Gt => ">",
Self::Amp => "&",
Self::Quot => """,
}
}
}
}
use std::str::FromStr;
#[macro_export]
macro_rules! declare_u64_hash {
($type_name:ident) => {
#[derive(
Hash,
Eq,
PartialEq,
Debug,
Ord,
PartialOrd,
Default,
Serialize,
Deserialize,
Copy,
Clone,
)]
#[repr(transparent)]
pub struct $type_name(pub u64);
impl $type_name {
#[inline(always)]
pub fn from_bytes(bytes: &[u8]) -> Self {
use std::{collections::hash_map::DefaultHasher, hash::Hasher};
let mut h = DefaultHasher::new();
h.write(bytes);
Self(h.finish())
}
#[inline(always)]
pub const fn to_be_bytes(self) -> [u8; 8] {
self.0.to_be_bytes()
}
#[inline(always)]
pub const fn from_be_bytes(b: [u8; 8]) -> Self {
Self(u64::from_be_bytes(b))
}
#[inline(always)]
pub const fn is_null(self) -> bool {
self.0 == 0
}
}
impl std::fmt::Display for $type_name {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{}", self.0)
}
}
impl From<&[u8]> for $type_name {
fn from(bytes: &[u8]) -> Self {
Self::from_bytes(bytes)
}
}
#[cfg(feature = "sqlite3")]
impl rusqlite::types::ToSql for $type_name {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
Ok(rusqlite::types::ToSqlOutput::from(self.0 as i64))
}
}
#[cfg(feature = "sqlite3")]
impl rusqlite::types::FromSql for $type_name {
fn column_result(
value: rusqlite::types::ValueRef,
) -> rusqlite::types::FromSqlResult<Self> {
use rusqlite::types::{FromSqlError, ValueRef};
match value {
ValueRef::Integer(b) => Ok(Self(b as u64)),
ValueRef::Blob(slice) => {
let Ok(be): ::std::result::Result<[u8; 8], _> = slice.try_into() else {
return Err(FromSqlError::InvalidType);
};
Ok(Self::from_be_bytes(be))
}
_other => Err(FromSqlError::InvalidType),
}
}
}
};
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub enum SortOrder {
Asc,
#[default]
Desc,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub enum SortField {
Subject,
#[default]
Date,
}
impl FromStr for SortField {
type Err = ();
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.trim().to_ascii_lowercase().as_str() {
"subject" | "s" | "sub" | "sbj" | "subj" => Ok(Self::Subject),
"date" | "d" => Ok(Self::Date),
_ => Err(()),
}
}
}
impl FromStr for SortOrder {
type Err = ();
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.trim().to_ascii_lowercase().as_str() {
"asc" => Ok(Self::Asc),
"desc" => Ok(Self::Desc),
_ => Err(()),
}
}
}
impl std::ops::Not for SortOrder {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Self::Asc => Self::Desc,
Self::Desc => Self::Asc,
}
}
}
pub mod hostname {
use std::io::Read;
use crate::{
error::{Error, ErrorKind, Result},
src_err_arc_wrap,
};
pub fn hostname() -> Result<std::ffi::OsString> {
let retval = nix::unistd::gethostname().map_err(|err| {
Error::new("Could not discover local hostname")
.set_source(Some(src_err_arc_wrap! {err}))
.set_kind(ErrorKind::Platform)
});
if retval.is_err() {
let mut hostn_buf = String::with_capacity(256);
if matches!(
std::fs::File::open("/etc/hostname")
.ok()
.and_then(|mut f| f.read_to_string(&mut hostn_buf).ok()),
Some(n) if n > 0
) {
return Ok(hostn_buf.into());
}
}
retval
}
}
#[macro_export]
macro_rules! identify {
($f:tt$($t:tt)*) => {{
const fn __debugify() -> ::core::marker::PhantomData<$f$($t)*> {
core::marker::PhantomData
}
let _: ::core::marker::PhantomData<Self> = __debugify();
stringify!($f$($t)*)
}};
}