identity_core/common/
string_or_url.rs1use std::convert::Infallible;
5use std::fmt::Display;
6use std::str::FromStr;
7
8use serde::Deserialize;
9use serde::Serialize;
10
11use super::Url;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(untagged)]
16pub enum StringOrUrl {
17 Url(Url),
19 String(String),
21}
22
23impl StringOrUrl {
24 pub fn parse(s: &str) -> Result<Self, Infallible> {
26 s.parse()
27 }
28 pub fn as_url(&self) -> Option<&Url> {
30 match self {
31 Self::Url(url) => Some(url),
32 _ => None,
33 }
34 }
35
36 pub fn as_string(&self) -> Option<&str> {
38 match self {
39 Self::String(s) => Some(s),
40 _ => None,
41 }
42 }
43
44 pub fn is_url(&self) -> bool {
46 matches!(self, Self::Url(_))
47 }
48
49 pub fn is_string(&self) -> bool {
51 matches!(self, Self::String(_))
52 }
53}
54
55impl Default for StringOrUrl {
56 fn default() -> Self {
57 StringOrUrl::String(String::default())
58 }
59}
60
61impl Display for StringOrUrl {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Self::Url(url) => write!(f, "{url}"),
65 Self::String(s) => write!(f, "{s}"),
66 }
67 }
68}
69
70impl FromStr for StringOrUrl {
71 type Err = Infallible;
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 Ok(
75 s.parse::<Url>()
76 .map(Self::Url)
77 .unwrap_or_else(|_| Self::String(s.to_string())),
78 )
79 }
80}
81
82impl AsRef<str> for StringOrUrl {
83 fn as_ref(&self) -> &str {
84 match self {
85 Self::String(s) => s,
86 Self::Url(url) => url.as_str(),
87 }
88 }
89}
90
91impl From<Url> for StringOrUrl {
92 fn from(value: Url) -> Self {
93 Self::Url(value)
94 }
95}
96
97impl From<String> for StringOrUrl {
98 fn from(value: String) -> Self {
99 Self::String(value)
100 }
101}
102
103impl From<StringOrUrl> for String {
104 fn from(value: StringOrUrl) -> Self {
105 match value {
106 StringOrUrl::String(s) => s,
107 StringOrUrl::Url(url) => url.into_string(),
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[derive(Debug, Serialize, Deserialize)]
117 struct TestData {
118 string_or_url: StringOrUrl,
119 }
120
121 impl Default for TestData {
122 fn default() -> Self {
123 Self {
124 string_or_url: StringOrUrl::Url(TEST_URL.parse().unwrap()),
125 }
126 }
127 }
128
129 const TEST_URL: &str = "file:///tmp/file.txt";
130
131 #[test]
132 fn deserialization_works() {
133 let test_data: TestData = serde_json::from_value(serde_json::json!({ "string_or_url": TEST_URL })).unwrap();
134 let target_url: Url = TEST_URL.parse().unwrap();
135 assert_eq!(test_data.string_or_url.as_url(), Some(&target_url));
136 }
137
138 #[test]
139 fn serialization_works() {
140 assert_eq!(
141 serde_json::to_value(TestData::default()).unwrap(),
142 serde_json::json!({ "string_or_url": TEST_URL })
143 )
144 }
145
146 #[test]
147 fn parsing_works() {
148 assert!(TEST_URL.parse::<StringOrUrl>().unwrap().is_url());
149 assert!("I'm a random string :)".parse::<StringOrUrl>().unwrap().is_string());
150 }
151}