1use std::{fmt, ops};
4
5use serde::{Deserialize, Serialize};
6use zng_txt::Txt;
7
8#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
15pub struct ApiExtensionPayload(#[serde(with = "serde_bytes")] pub Vec<u8>);
16impl ApiExtensionPayload {
17 pub fn serialize<T: Serialize>(payload: &T) -> Result<Self, bincode::error::EncodeError> {
19 bincode::serde::encode_to_vec(payload, bincode::config::standard()).map(Self)
20 }
21
22 pub fn deserialize<T: serde::de::DeserializeOwned>(&self) -> Result<T, ApiExtensionRecvError> {
24 if let Some((id, error)) = self.parse_invalid_request() {
25 Err(ApiExtensionRecvError::InvalidRequest {
26 extension_id: id,
27 error: Txt::from_str(error),
28 })
29 } else if let Some(id) = self.parse_unknown_extension() {
30 Err(ApiExtensionRecvError::UnknownExtension { extension_id: id })
31 } else {
32 match bincode::serde::decode_from_slice(&self.0, bincode::config::standard()) {
33 Ok((r, _)) => Ok(r),
34 Err(e) => Err(ApiExtensionRecvError::Deserialize(e)),
35 }
36 }
37 }
38
39 pub const fn empty() -> Self {
41 Self(vec![])
42 }
43
44 pub fn unknown_extension(extension_id: ApiExtensionId) -> Self {
48 Self(format!("zng-view-api.unknown_extension;id={extension_id}").into_bytes())
49 }
50
51 pub fn invalid_request(extension_id: ApiExtensionId, error: impl fmt::Display) -> Self {
55 Self(format!("zng-view-api.invalid_request;id={extension_id};error={error}").into_bytes())
56 }
57
58 pub fn parse_unknown_extension(&self) -> Option<ApiExtensionId> {
65 let p = self.0.strip_prefix(b"zng-view-api.unknown_extension;")?;
66 if let Some(p) = p.strip_prefix(b"id=")
67 && let Ok(id_str) = std::str::from_utf8(p)
68 {
69 return match id_str.parse::<ApiExtensionId>() {
70 Ok(id) => Some(id),
71 Err(id) => Some(id),
72 };
73 }
74 Some(ApiExtensionId::INVALID)
75 }
76
77 pub fn parse_invalid_request(&self) -> Option<(ApiExtensionId, &str)> {
84 let p = self.0.strip_prefix(b"zng-view-api.invalid_request;")?;
85 if let Some(p) = p.strip_prefix(b"id=")
86 && let Some(id_end) = p.iter().position(|&b| b == b';')
87 && let Ok(id_str) = std::str::from_utf8(&p[..id_end])
88 {
89 let id = match id_str.parse::<ApiExtensionId>() {
90 Ok(id) => id,
91 Err(id) => id,
92 };
93 if let Some(p) = p[id_end..].strip_prefix(b";error=")
94 && let Ok(err_str) = std::str::from_utf8(p)
95 {
96 return Some((id, err_str));
97 }
98 return Some((id, "invalid request, corrupted payload, unknown error"));
99 }
100 Some((
101 ApiExtensionId::INVALID,
102 "invalid request, corrupted payload, unknown extension_id and error",
103 ))
104 }
105}
106impl fmt::Debug for ApiExtensionPayload {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(f, "ExtensionPayload({} bytes)", self.0.len())
109 }
110}
111
112#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
119pub struct ApiExtensionName {
120 name: Txt,
121}
122impl ApiExtensionName {
123 pub fn new(name: impl Into<Txt>) -> Result<Self, ApiExtensionNameError> {
127 let name = name.into();
128 Self::new_impl(name)
129 }
130 fn new_impl(name: Txt) -> Result<ApiExtensionName, ApiExtensionNameError> {
131 if name.is_empty() {
132 return Err(ApiExtensionNameError::NameCannotBeEmpty);
133 }
134 for (i, c) in name.char_indices() {
135 if i == 0 {
136 if !c.is_ascii_alphabetic() {
137 return Err(ApiExtensionNameError::NameCannotStartWithChar(c));
138 }
139 } else if !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.' {
140 return Err(ApiExtensionNameError::NameInvalidChar(c));
141 }
142 }
143
144 Ok(Self { name })
145 }
146}
147impl fmt::Debug for ApiExtensionName {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 fmt::Debug::fmt(&self.name, f)
150 }
151}
152impl fmt::Display for ApiExtensionName {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 fmt::Display::fmt(&self.name, f)
155 }
156}
157impl ops::Deref for ApiExtensionName {
158 type Target = str;
159
160 fn deref(&self) -> &Self::Target {
161 self.name.as_str()
162 }
163}
164impl From<&'static str> for ApiExtensionName {
165 fn from(value: &'static str) -> Self {
166 Self::new(value).unwrap()
167 }
168}
169
170#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
172#[non_exhaustive]
173pub enum ApiExtensionNameError {
174 NameCannotBeEmpty,
176 NameCannotStartWithChar(char),
178 NameInvalidChar(char),
180}
181impl fmt::Display for ApiExtensionNameError {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 match self {
184 ApiExtensionNameError::NameCannotBeEmpty => write!(f, "API extension name cannot be empty"),
185 ApiExtensionNameError::NameCannotStartWithChar(c) => {
186 write!(f, "API cannot start with '{c}', name pattern `[a-zA-Z][a-zA-Z0-9-_.]`")
187 }
188 ApiExtensionNameError::NameInvalidChar(c) => write!(f, "API cannot contain '{c}', name pattern `[a-zA-Z][a-zA-Z0-9-_.]`"),
189 }
190 }
191}
192impl std::error::Error for ApiExtensionNameError {}
193
194#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
196pub struct ApiExtensions(Vec<ApiExtensionName>);
197impl ops::Deref for ApiExtensions {
198 type Target = [ApiExtensionName];
199
200 fn deref(&self) -> &Self::Target {
201 &self.0
202 }
203}
204impl ApiExtensions {
205 pub fn new() -> Self {
207 Self::default()
208 }
209
210 pub fn id(&self, ext: &ApiExtensionName) -> Option<ApiExtensionId> {
219 self.0.iter().position(|e| e == ext).map(ApiExtensionId::from_index)
220 }
221
222 pub fn insert(&mut self, ext: ApiExtensionName) -> Result<ApiExtensionId, ApiExtensionId> {
226 if let Some(key) = self.id(&ext) {
227 Err(key)
228 } else {
229 let key = self.0.len();
230 self.0.push(ext);
231 Ok(ApiExtensionId::from_index(key))
232 }
233 }
234}
235
236#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
238#[serde(transparent)]
239pub struct ApiExtensionId(u32);
240impl fmt::Debug for ApiExtensionId {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 if *self == Self::INVALID {
243 if f.alternate() {
244 write!(f, "ApiExtensionId::")?;
245 }
246 write!(f, "INVALID")
247 } else {
248 write!(f, "ApiExtensionId({})", self.0 - 1)
249 }
250 }
251}
252impl fmt::Display for ApiExtensionId {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 if *self == Self::INVALID {
255 write!(f, "invalid")
256 } else {
257 write!(f, "{}", self.0 - 1)
258 }
259 }
260}
261impl ApiExtensionId {
262 pub const INVALID: Self = Self(0);
264
265 pub fn index(self) -> usize {
271 self.0.checked_sub(1).expect("invalid id") as _
272 }
273
274 pub fn from_index(idx: usize) -> Self {
280 if idx > (u32::MAX - 1) as _ {
281 panic!("index out-of-bounds")
282 }
283 Self(idx as u32 + 1)
284 }
285}
286impl std::str::FromStr for ApiExtensionId {
287 type Err = Self;
288
289 fn from_str(s: &str) -> Result<Self, Self::Err> {
290 match s.parse::<u32>() {
291 Ok(i) => {
292 let r = Self::from_index(i as _);
293 if r == Self::INVALID { Err(r) } else { Ok(r) }
294 }
295 Err(_) => Err(Self::INVALID),
296 }
297 }
298}
299
300#[derive(Debug)]
302#[non_exhaustive]
303pub enum ApiExtensionRecvError {
304 UnknownExtension {
306 extension_id: ApiExtensionId,
310 },
311 InvalidRequest {
313 extension_id: ApiExtensionId,
317 error: Txt,
319 },
320 Deserialize(bincode::error::DecodeError),
322}
323impl fmt::Display for ApiExtensionRecvError {
324 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325 match self {
326 ApiExtensionRecvError::UnknownExtension { extension_id } => write!(f, "invalid API request for unknown id {extension_id:?}"),
327 ApiExtensionRecvError::InvalidRequest { extension_id, error } => {
328 write!(f, "invalid API request for extension id {extension_id:?}, {error}")
329 }
330 ApiExtensionRecvError::Deserialize(e) => write!(f, "API extension response failed to deserialize, {e}"),
331 }
332 }
333}
334impl std::error::Error for ApiExtensionRecvError {
335 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
336 if let Self::Deserialize(e) = self { Some(e) } else { None }
337 }
338}