// SPDX-FileCopyrightText: 2025 Ɓukasz Niemier <#@hauleth.dev> // // SPDX-License-Identifier: EUPL-1.2 use std::fmt; use std::str::FromStr; use serde::{Deserialize, Deserializer, de}; pub fn string_or_struct<'de, T, D>(deserializer: D) -> Result where T: Deserialize<'de> + FromStr, D: Deserializer<'de>, ::Err: fmt::Display, { // This is a Visitor that forwards string types to T's `FromStr` impl and // forwards map types to T's `Deserialize` impl. The `PhantomData` is to // keep the compiler from complaining about T being an unused generic type // parameter. We need T in order to know the Value type for the Visitor // impl. struct StringOrStruct(std::marker::PhantomData T>); impl<'de, T> de::Visitor<'de> for StringOrStruct where T: Deserialize<'de> + FromStr, ::Err: fmt::Display, { type Value = T; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("string or map") } fn visit_str(self, value: &str) -> Result where E: de::Error, { FromStr::from_str(value).map_err(de::Error::custom) } fn visit_map(self, map: M) -> Result where M: de::MapAccess<'de>, { // `MapAccessDeserializer` is a wrapper that turns a `MapAccess` // into a `Deserializer`, allowing it to be used as the input to T's // `Deserialize` implementation. T then deserializes itself using // the entries from the map visitor. Deserialize::deserialize(de::value::MapAccessDeserializer::new(map)) } } deserializer.deserialize_any(StringOrStruct(Default::default())) } pub fn from_str<'de, T, D>(deserializer: D) -> Result where T: FromStr, D: Deserializer<'de>, ::Err: fmt::Display, { struct DeFromStr(std::marker::PhantomData); impl de::Visitor<'_> for DeFromStr where T: FromStr, ::Err: fmt::Display, { type Value = T; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("string or map") } fn visit_str(self, value: &str) -> Result where E: de::Error, { FromStr::from_str(value).map_err(de::Error::custom) } } deserializer.deserialize_any(DeFromStr(Default::default())) }