1use std::{borrow::Borrow, fmt, str::FromStr};
2
3use indexmap::{IndexMap, IndexSet};
4use serde::{Deserialize, Serialize, de::Error as _};
5use serde_with::{DeserializeFromStr, SerializeDisplay, skip_serializing_none};
6use url::Url;
7
8use crate::{Action, IsEmpty, TargetType};
9
10mod ipnet;
11mod mac_addr;
12mod nsid;
13pub mod primitive;
14mod time;
15mod value;
16mod version;
17
18pub use ipnet::{Ipv4Net, Ipv6Net};
19pub use mac_addr::{MacAddr, MacAddr6, MacAddr8};
20pub use nsid::Nsid;
21pub use time::{DateTime, Duration};
22pub use value::Value;
23pub use version::Version;
24
25pub type ActionTargets = IndexMap<Action, IndexSet<TargetType<'static>>>;
26
27pub type CommandId = String;
28
29#[derive(
30 Debug, Clone, SerializeDisplay, DeserializeFromStr, PartialEq, Eq, PartialOrd, Ord, Hash,
31)]
32pub struct DomainName(String);
33
34impl fmt::Display for DomainName {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 self.0.fmt(f)
37 }
38}
39
40impl FromStr for DomainName {
41 type Err = crate::error::ValidationError;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 Ok(Self(s.to_string()))
45 }
46}
47
48#[derive(
49 Debug, Clone, SerializeDisplay, DeserializeFromStr, PartialEq, Eq, PartialOrd, Ord, Hash,
50)]
51pub struct EmailAddr(String);
52
53impl fmt::Display for EmailAddr {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 self.0.fmt(f)
56 }
57}
58
59impl FromStr for EmailAddr {
60 type Err = crate::error::ValidationError;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 Ok(Self(s.to_string()))
64 }
65}
66
67#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
68#[serde(transparent)]
69pub struct Extensions<V>(IndexMap<Nsid, V>);
70
71impl<V> Extensions<V> {
72 pub fn len(&self) -> usize {
73 self.0.len()
74 }
75
76 pub fn is_empty(&self) -> bool {
77 self.0.is_empty()
78 }
79
80 pub fn contains(&self, key: &impl Borrow<str>) -> bool {
81 self.0.contains_key(key.borrow())
82 }
83
84 pub fn get_raw(&self, key: &impl Borrow<str>) -> Option<&V> {
85 self.0.get(key.borrow())
86 }
87
88 pub fn insert(&mut self, key: Nsid, value: V) -> Option<V> {
89 self.0.insert(key, value)
90 }
91}
92
93impl<V: Value + Clone> Extensions<V> {
94 pub fn get<'de, T: Deserialize<'de>>(
96 &'de self,
97 key: &impl Borrow<str>,
98 ) -> Option<Result<T, V::Error>> {
99 self.get_raw(key).map(|v| v.to_typed())
100 }
101
102 pub fn require<'de, T: Deserialize<'de>>(
105 &'de self,
106 key: &impl Borrow<str>,
107 ) -> Result<T, V::Error> {
108 self.get::<T>(key)
109 .transpose()?
110 .ok_or_else(|| V::Error::custom(format!("extension {} is required", key.borrow())))
111 }
112}
113
114impl<V> Default for Extensions<V> {
115 fn default() -> Self {
116 Self(Default::default())
117 }
118}
119
120impl<V> IsEmpty for Extensions<V> {
121 fn is_empty(&self) -> bool {
122 self.0.is_empty()
123 }
124}
125
126impl<'de, V: Deserialize<'de>> Deserialize<'de> for Extensions<V> {
127 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128 where
129 D: serde::Deserializer<'de>,
130 {
131 let map = IndexMap::<Nsid, V>::deserialize(deserializer)?;
132 Ok(Self(map))
133 }
134}
135
136impl<V> IntoIterator for Extensions<V> {
137 type Item = (Nsid, V);
138 type IntoIter = indexmap::map::IntoIter<Nsid, V>;
139
140 fn into_iter(self) -> Self::IntoIter {
141 self.0.into_iter()
142 }
143}
144
145impl<'a, V> IntoIterator for &'a Extensions<V> {
146 type Item = (&'a Nsid, &'a V);
147 type IntoIter = indexmap::map::Iter<'a, Nsid, V>;
148
149 fn into_iter(self) -> Self::IntoIter {
150 self.0.iter()
151 }
152}
153
154impl<V> FromIterator<(Nsid, V)> for Extensions<V> {
155 fn from_iter<T: IntoIterator<Item = (Nsid, V)>>(iter: T) -> Self {
156 Self(iter.into_iter().collect())
157 }
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
161#[serde(rename_all = "snake_case")]
162#[non_exhaustive]
163pub enum Feature {
164 Versions,
165 Profiles,
166 Pairs,
167 RateLimit,
168}
169
170#[skip_serializing_none]
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash)]
172pub struct Hashes {
173 pub md5: Option<String>,
174 pub sha1: Option<String>,
175 pub sha256: Option<String>,
176}
177
178impl IsEmpty for Hashes {
179 fn is_empty(&self) -> bool {
180 self.md5.is_none() && self.sha1.is_none() && self.sha256.is_none()
181 }
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
185#[serde(rename_all = "snake_case")]
186pub enum Payload {
187 #[serde(rename = "bin")]
188 Binary(Vec<u8>),
189 Url(Url),
190}
191
192pub type Port = u16;
193
194#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
195#[serde(rename_all = "snake_case")]
196pub enum ResponseType {
197 None,
198 Ack,
199 Status,
200 Complete,
201}
202
203impl ResponseType {
204 pub fn requires_request_id(self) -> bool {
205 !matches!(self, ResponseType::None)
206 }
207}