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