use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{DeserializeAs, DisplayFromStr, Map, SerializeAs, schemars_1::JsonSchemaAs};
use std::{
borrow::Cow,
collections::{BTreeMap, BTreeSet, HashMap},
fmt,
marker::PhantomData,
time::{Duration, Instant},
};
pub mod backon;
pub mod otlp;
pub mod regex;
pub mod reqwest;
pub mod tokio;
pub mod tracing;
macro_rules! serde {
($($item:item)*) => {
$(
#[::serde_with::skip_serializing_none]
#[::serde_with::serde_as]
#[derive(::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, Clone, Debug)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
$item
)*
};
}
pub(crate) use serde;
macro_rules! convert_enum {
($(
$left:ty = $right:ty $([$($generics:tt)*])? {
$( [$($l:tt)*] = [$($r:tt)*] )*
}
)*) => {$(
impl $(<$($generics)*>)? From<$left> for $right {
fn from(value: $left) -> $right {
match value {
$( $($l)* => $($r)* ),*
}
}
}
impl $(<$($generics)*>)? From<$right> for $left {
fn from(value: $right) -> $left {
match value {
$( $($r)* => $($l)* ),*
}
}
}
)*};
}
pub(crate) use convert_enum;
#[cfg(test)]
pub(crate) use {expect_test::expect, serde_json::json};
#[cfg(test)]
#[track_caller]
fn assert_round_trip<T: serde::de::DeserializeOwned + Serialize + std::fmt::Debug>(
json: serde_json::Value,
exp: expect_test::Expect,
) {
let de = serde_path_to_error::deserialize::<_, T>(&json).expect("couldn't deserialize");
let rt = serde_json::to_value(&de).expect("couldn't serialize");
exp.assert_debug_eq(&de);
assert_eq!(json, rt, "did not round-trip");
}
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
trait Ext: Sized {
fn tap_opt<T>(self, opt: Option<T>, f: impl FnOnce(Self, T) -> Self) -> Self {
match opt {
Some(it) => f(self, it),
None => self,
}
}
fn try_tap_opt<T, E>(
self,
opt: Option<T>,
f: impl FnOnce(Self, T) -> Result<Self, E>,
) -> Result<Self, E> {
match opt {
Some(it) => f(self, it),
None => Ok(self),
}
}
}
impl<T> Ext for T {}
struct HumanTime;
impl SerializeAs<Duration> for HumanTime {
fn serialize_as<S: Serializer>(this: &Duration, s: S) -> Result<S::Ok, S::Error> {
s.collect_str(&humantime::format_duration(*this))
}
}
impl<'de> DeserializeAs<'de, Duration> for HumanTime {
fn deserialize_as<D: Deserializer<'de>>(d: D) -> Result<Duration, D::Error> {
let s = String::deserialize(d)?;
humantime::parse_duration(&s).map_err(|e| {
struct Expected(humantime::DurationError);
impl serde::de::Expected for Expected {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self(e) = self;
f.write_fmt(format_args!("a human-readable duration ({e})"))
}
}
serde::de::Error::invalid_value(serde::de::Unexpected::Str(&s), &Expected(e))
})
}
}
impl JsonSchemaAs<Duration> for HumanTime {
fn schema_name() -> Cow<'static, str> {
"HumanTime<Duration>".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"title": "HumanTime",
"type": "string",
"description": "a human-readable duration",
"examples": [
"1 day 3 hours"
],
})
}
fn inline_schema() -> bool {
false
}
fn schema_id() -> Cow<'static, str> {
format!(
"{}::{}",
module_path!(),
<Self as JsonSchemaAs<Duration>>::schema_name()
)
.into()
}
}
serde! {
#[serde(untagged, bound(
serialize = "B: AsRef<[u8]>, S: Serialize",
deserialize = "B: AsRef<[u8]> + From<Vec<u8>>, S: Deserialize<'de>"
))]
pub enum OrHex<S, B> {
Inline(S),
Hex { #[serde_as(as = "serde_with::hex::Hex")] hex: B },
}
#[serde(untagged)]
enum Untagged<L, R> {
Left(L),
Right(R),
}
#[serde(from = "Untagged<False, T>", into = "Untagged<False, T>")]
#[serde(bound(serialize = "T: Clone + Serialize"))]
#[schemars(with = "Untagged<False, T>")]
pub enum FalseOr<T> {
False,
Or(T)
}
#[serde(from = "Untagged<True, T>", into = "Untagged<True, T>")]
#[serde(bound(serialize = "T: Clone + Serialize"))]
#[schemars(with = "Untagged<True, T>")]
pub enum TrueOr<T> {
True,
Or(T)
}
#[serde(untagged, expecting = "a map of string to string, or a sequence of pairs of string")]
enum _HeaderMap {
Map(
#[serde_as(as = "Map<DisplayFromStr, HeaderValue>")]
Vec<(http::HeaderName, http::HeaderValue)>
),
Seq(
#[serde_as(as = "Vec<(DisplayFromStr, HeaderValue)>")]
Vec<(http::HeaderName, http::HeaderValue)>
)
}
#[derive(Default)]
pub struct OpenOptions {
pub create: Option<bool>,
pub create_new: Option<bool>,
pub append: Option<bool>,
pub truncate: Option<bool>,
pub read: Option<bool>,
pub write: Option<bool>,
}
}
impl OpenOptions {
pub fn build(self) -> std::fs::OpenOptions {
let Self {
create,
create_new,
append,
truncate,
read,
write,
} = self;
let mut o = std::fs::OpenOptions::new();
(&mut o)
.tap_opt(create, |b, v| b.create(v))
.tap_opt(create_new, |b, v| b.create_new(v))
.tap_opt(append, |b, v| b.append(v))
.tap_opt(truncate, |b, v| b.truncate(v))
.tap_opt(read, |b, v| b.read(v))
.tap_opt(write, |b, v| b.write(v));
o
}
}
convert_enum! {
FalseOr<T> = Untagged<False, T> [T] {
[FalseOr::False] = [Untagged::Left(False)]
[FalseOr::Or(it)] = [Untagged::Right(it)]
}
TrueOr<T> = Untagged<True, T> [T] {
[TrueOr::True] = [Untagged::Left(True)]
[TrueOr::Or(it)] = [Untagged::Right(it)]
}
}
impl<T> FalseOr<T> {
fn into_option(self) -> Option<T> {
match self {
FalseOr::False => None,
FalseOr::Or(it) => Some(it),
}
}
}
impl<T> TrueOr<T> {
fn into_option(self) -> Option<T> {
match self {
TrueOr::True => None,
TrueOr::Or(it) => Some(it),
}
}
}
struct FalseOrWith<A, J>(PhantomData<(A, J)>);
impl<A: SerializeAs<T>, T, J> SerializeAs<FalseOr<T>> for FalseOrWith<A, J> {
fn serialize_as<S: Serializer>(this: &FalseOr<T>, s: S) -> Result<S::Ok, S::Error> {
match this {
FalseOr::False => false.serialize(s),
FalseOr::Or(it) => A::serialize_as(it, s),
}
}
}
#[derive(Deserialize, Serialize)]
enum Never {}
impl<'de, A: DeserializeAs<'de, T>, T, J> DeserializeAs<'de, FalseOr<T>> for FalseOrWith<A, J> {
fn deserialize_as<D: Deserializer<'de>>(d: D) -> Result<FalseOr<T>, D::Error> {
#[derive(Deserialize)]
#[serde(bound = "A: DeserializeAs<'de, T>")]
enum FalseOrWithUntagged<T, A> {
False(False),
Or(#[serde(with = "serde_with::As::<A>")] T),
As { never: Never, _a: PhantomData<A> },
}
Ok(match FalseOrWithUntagged::<T, A>::deserialize(d)? {
FalseOrWithUntagged::False(False) => FalseOr::False,
FalseOrWithUntagged::Or(it) => FalseOr::Or(it),
FalseOrWithUntagged::As { never, _a } => match never {},
})
}
}
impl<A: JsonSchemaAs<J>, T, J> JsonSchemaAs<T> for FalseOrWith<A, J> {
fn schema_name() -> Cow<'static, str> {
format!("FalseOr<{}>", A::schema_name()).into()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"anyOf": [
{
"const": false
},
A::json_schema(generator)
]
})
}
fn schema_id() -> Cow<'static, str> {
format!(
"{}::{}",
module_path!(),
<Self as JsonSchemaAs<T>>::schema_name()
)
.into()
}
}
struct HeaderValue;
impl SerializeAs<http::HeaderValue> for HeaderValue {
fn serialize_as<S: Serializer>(this: &http::HeaderValue, s: S) -> Result<S::Ok, S::Error> {
match this.to_str() {
Ok(it) => OrHex::Inline(it),
Err(_) => OrHex::Hex {
hex: this.as_bytes(),
},
}
.serialize(s)
}
}
impl<'de> DeserializeAs<'de, http::HeaderValue> for HeaderValue {
fn deserialize_as<D: Deserializer<'de>>(d: D) -> Result<http::HeaderValue, D::Error> {
match OrHex::<String, Vec<_>>::deserialize(d)? {
OrHex::Inline(it) => http::HeaderValue::try_from(it),
OrHex::Hex { hex } => http::HeaderValue::try_from(hex),
}
.map_err(serde::de::Error::custom)
}
}
impl JsonSchemaAs<http::HeaderValue> for HeaderValue {
fn schema_name() -> Cow<'static, str> {
"HeaderValue".into()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
OrHex::<String, Vec<u8>>::json_schema(generator)
}
fn schema_id() -> Cow<'static, str> {
format!("http::{}", Self::schema_name()).into()
}
}
struct HeaderMap;
impl SerializeAs<http::HeaderMap> for HeaderMap {
fn serialize_as<S: Serializer>(this: &http::HeaderMap, s: S) -> Result<S::Ok, S::Error> {
_HeaderMap::Seq(this.iter().map(|(k, v)| (k.clone(), v.clone())).collect()).serialize(s)
}
}
impl<'de> DeserializeAs<'de, http::HeaderMap> for HeaderMap {
fn deserialize_as<D: Deserializer<'de>>(d: D) -> Result<http::HeaderMap, D::Error> {
let (_HeaderMap::Map(v) | _HeaderMap::Seq(v)) = Deserialize::deserialize(d)?;
Ok(http::HeaderMap::from_iter(v))
}
}
impl JsonSchemaAs<http::HeaderMap> for HeaderMap {
fn schema_name() -> Cow<'static, str> {
"HeaderMap".into()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
_HeaderMap::json_schema(generator)
}
fn schema_id() -> Cow<'static, str> {
format!("http::{}", Self::schema_name()).into()
}
}
struct BoundedFloat<const LO: i64, const HI: i64>;
impl<'de, const LO: i64, const HI: i64, T: Deserialize<'de> + Into<f64> + Clone>
DeserializeAs<'de, T> for BoundedFloat<LO, HI>
{
fn deserialize_as<D: Deserializer<'de>>(d: D) -> Result<T, D::Error> {
let f = T::deserialize(d)?;
let rng = (LO as f64)..=(HI as f64);
match rng.contains(&f.clone().into()) {
true => Ok(f),
false => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Float(f.into()),
&&*format!("a float in the range {rng:?}"),
)),
}
}
}
impl<const LO: i64, const HI: i64, T: Serialize> SerializeAs<T> for BoundedFloat<LO, HI> {
fn serialize_as<S: Serializer>(this: &T, s: S) -> Result<S::Ok, S::Error> {
this.serialize(s)
}
}
impl<const LO: i64, const HI: i64, T> JsonSchemaAs<T> for BoundedFloat<LO, HI> {
fn schema_name() -> Cow<'static, str> {
format!("BoundedFloat<{LO}, {HI}>").into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "number",
"minimum": LO,
"maximum": HI,
})
}
fn inline_schema() -> bool {
true
}
fn schema_id() -> Cow<'static, str> {
format!(
"{}::{}",
module_path!(),
<Self as JsonSchemaAs<T>>::schema_name()
)
.into()
}
}
trait ToUnexpected {
fn to_unexpected(&self) -> serde::de::Unexpected<'_>;
}
impl<T: ToUnexpected + ?Sized> ToUnexpected for &T {
fn to_unexpected(&self) -> serde::de::Unexpected<'_> {
T::to_unexpected(self)
}
}
impl ToUnexpected for bool {
fn to_unexpected(&self) -> serde::de::Unexpected<'_> {
serde::de::Unexpected::Bool(*self)
}
}
impl ToUnexpected for String {
fn to_unexpected(&self) -> serde::de::Unexpected<'_> {
serde::de::Unexpected::Str(self)
}
}
impl ToUnexpected for str {
fn to_unexpected(&self) -> serde::de::Unexpected<'_> {
serde::de::Unexpected::Str(self)
}
}
macro_rules! literal {
($(
$(#[$meta:meta])*
$vis:vis struct $ident:ident($ty:ty = $val:expr);
)*) => {$(
$(#[$meta])*
$vis struct $ident;
impl Serialize for $ident {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
$val.serialize(s)
}
}
impl<'de> Deserialize<'de> for $ident {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let it = <$ty>::deserialize(d)?;
match it == $val {
true => Ok(Self),
false => Err(serde::de::Error::invalid_value(ToUnexpected::to_unexpected(&it), &&*format!("{}", $val)))
}
}
}
impl JsonSchema for $ident {
fn schema_name() -> Cow<'static, str> {
format!("Literal<{:?}>", $val).into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"const": $val
})
}
fn inline_schema() -> bool {
true
}
fn schema_id() -> Cow<'static, str> {
format!("{}::{}", module_path!(), Self::schema_name()).into()
}
}
)*};
}
literal! {
#[derive(Clone, Debug)]
struct True(bool = true);
#[derive(Clone, Debug)]
struct False(bool = false);
}
fn is_empty<T: IsEmpty>(this: &T) -> bool {
T::is_empty(this)
}
trait IsEmpty {
fn is_empty(&self) -> bool;
}
impl IsEmpty for String {
fn is_empty(&self) -> bool {
String::is_empty(self)
}
}
impl<T> IsEmpty for Vec<T> {
fn is_empty(&self) -> bool {
Vec::is_empty(self)
}
}
impl<K, V> IsEmpty for BTreeMap<K, V> {
fn is_empty(&self) -> bool {
BTreeMap::is_empty(self)
}
}
impl<T> IsEmpty for BTreeSet<T> {
fn is_empty(&self) -> bool {
BTreeSet::is_empty(self)
}
}
impl<K, V, S> IsEmpty for HashMap<K, V, S> {
fn is_empty(&self) -> bool {
HashMap::is_empty(self)
}
}