Fetch User Keys - simple tool for fetching SSH keys from various sources
1// SPDX-FileCopyrightText: 2025 Łukasz Niemier <#@hauleth.dev>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5use std::fmt;
6use std::str::FromStr;
7
8use serde::{de, Deserialize, Deserializer};
9
10pub fn string_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error>
11where
12 T: Deserialize<'de> + FromStr,
13 D: Deserializer<'de>,
14 <T as FromStr>::Err: fmt::Display,
15{
16 // This is a Visitor that forwards string types to T's `FromStr` impl and
17 // forwards map types to T's `Deserialize` impl. The `PhantomData` is to
18 // keep the compiler from complaining about T being an unused generic type
19 // parameter. We need T in order to know the Value type for the Visitor
20 // impl.
21 struct StringOrStruct<T>(std::marker::PhantomData<fn() -> T>);
22
23 impl<'de, T> de::Visitor<'de> for StringOrStruct<T>
24 where
25 T: Deserialize<'de> + FromStr,
26 <T as FromStr>::Err: fmt::Display,
27 {
28 type Value = T;
29
30 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
31 formatter.write_str("string or map")
32 }
33
34 fn visit_str<E>(self, value: &str) -> Result<T, E>
35 where
36 E: de::Error,
37 {
38 FromStr::from_str(value).map_err(de::Error::custom)
39 }
40
41 fn visit_map<M>(self, map: M) -> Result<T, M::Error>
42 where
43 M: de::MapAccess<'de>,
44 {
45 // `MapAccessDeserializer` is a wrapper that turns a `MapAccess`
46 // into a `Deserializer`, allowing it to be used as the input to T's
47 // `Deserialize` implementation. T then deserializes itself using
48 // the entries from the map visitor.
49 Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
50 }
51 }
52
53 deserializer.deserialize_any(StringOrStruct(Default::default()))
54}
55
56pub fn from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
57where
58 T: FromStr,
59 D: Deserializer<'de>,
60 <T as FromStr>::Err: fmt::Display,
61{
62 struct DeFromStr<T>(std::marker::PhantomData<T>);
63
64 impl<'de, T> de::Visitor<'de> for DeFromStr<T>
65 where
66 T: FromStr,
67 <T as FromStr>::Err: fmt::Display,
68 {
69 type Value = T;
70
71 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
72 formatter.write_str("string or map")
73 }
74
75 fn visit_str<E>(self, value: &str) -> Result<T, E>
76 where
77 E: de::Error,
78 {
79 FromStr::from_str(value).map_err(de::Error::custom)
80 }
81 }
82
83 deserializer.deserialize_any(DeFromStr(Default::default()))
84}