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) -> bincode::Result<Self> {
21 bincode::serialize(payload).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 bincode::deserialize(&self.0).map_err(ApiExtensionRecvError::Deserialize)
35 }
36 }
37
38 pub const fn empty() -> Self {
40 Self(vec![])
41 }
42
43 pub fn unknown_extension(extension_id: ApiExtensionId) -> Self {
47 Self(format!("zng-view-api.unknown_extension;id={extension_id}").into_bytes())
48 }
49
50 pub fn invalid_request(extension_id: ApiExtensionId, error: impl fmt::Display) -> Self {
54 Self(format!("zng-view-api.invalid_request;id={extension_id};error={error}").into_bytes())
55 }
56
57 pub fn parse_unknown_extension(&self) -> Option<ApiExtensionId> {
64 let p = self.0.strip_prefix(b"zng-view-api.unknown_extension;")?;
65 if let Some(p) = p.strip_prefix(b"id=") {
66 if let Ok(id_str) = std::str::from_utf8(p) {
67 return match id_str.parse::<ApiExtensionId>() {
68 Ok(id) => Some(id),
69 Err(id) => Some(id),
70 };
71 }
72 }
73 Some(ApiExtensionId::INVALID)
74 }
75
76 pub fn parse_invalid_request(&self) -> Option<(ApiExtensionId, &str)> {
83 let p = self.0.strip_prefix(b"zng-view-api.invalid_request;")?;
84 if let Some(p) = p.strip_prefix(b"id=") {
85 if let Some(id_end) = p.iter().position(|&b| b == b';') {
86 if let Ok(id_str) = std::str::from_utf8(&p[..id_end]) {
87 let id = match id_str.parse::<ApiExtensionId>() {
88 Ok(id) => id,
89 Err(id) => id,
90 };
91 if let Some(p) = p[id_end..].strip_prefix(b";error=") {
92 if let Ok(err_str) = std::str::from_utf8(p) {
93 return Some((id, err_str));
94 }
95 }
96 return Some((id, "invalid request, corrupted payload, unknown error"));
97 }
98 }
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)]
172pub enum ApiExtensionNameError {
173 NameCannotBeEmpty,
175 NameCannotStartWithChar(char),
177 NameInvalidChar(char),
179}
180impl fmt::Display for ApiExtensionNameError {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 match self {
183 ApiExtensionNameError::NameCannotBeEmpty => write!(f, "API extension name cannot be empty"),
184 ApiExtensionNameError::NameCannotStartWithChar(c) => {
185 write!(f, "API cannot start with '{c}', name pattern `[a-zA-Z][a-zA-Z0-9-_.]`")
186 }
187 ApiExtensionNameError::NameInvalidChar(c) => write!(f, "API cannot contain '{c}', name pattern `[a-zA-Z][a-zA-Z0-9-_.]`"),
188 }
189 }
190}
191impl std::error::Error for ApiExtensionNameError {}
192
193#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
195pub struct ApiExtensions(Vec<ApiExtensionName>);
196impl ops::Deref for ApiExtensions {
197 type Target = [ApiExtensionName];
198
199 fn deref(&self) -> &Self::Target {
200 &self.0
201 }
202}
203impl ApiExtensions {
204 pub fn new() -> Self {
206 Self::default()
207 }
208
209 pub fn id(&self, ext: &ApiExtensionName) -> Option<ApiExtensionId> {
218 self.0.iter().position(|e| e == ext).map(ApiExtensionId::from_index)
219 }
220
221 pub fn insert(&mut self, ext: ApiExtensionName) -> Result<ApiExtensionId, ApiExtensionId> {
225 if let Some(key) = self.id(&ext) {
226 Err(key)
227 } else {
228 let key = self.0.len();
229 self.0.push(ext);
230 Ok(ApiExtensionId::from_index(key))
231 }
232 }
233}
234
235#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
237#[serde(transparent)]
238pub struct ApiExtensionId(u32);
239impl fmt::Debug for ApiExtensionId {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 if *self == Self::INVALID {
242 if f.alternate() {
243 write!(f, "ApiExtensionId::")?;
244 }
245 write!(f, "INVALID")
246 } else {
247 write!(f, "ApiExtensionId({})", self.0 - 1)
248 }
249 }
250}
251impl fmt::Display for ApiExtensionId {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 if *self == Self::INVALID {
254 write!(f, "invalid")
255 } else {
256 write!(f, "{}", self.0 - 1)
257 }
258 }
259}
260impl ApiExtensionId {
261 pub const INVALID: Self = Self(0);
263
264 pub fn index(self) -> usize {
270 self.0.checked_sub(1).expect("invalid id") as _
271 }
272
273 pub fn from_index(idx: usize) -> Self {
279 if idx > (u32::MAX - 1) as _ {
280 panic!("index out-of-bounds")
281 }
282 Self(idx as u32 + 1)
283 }
284}
285impl std::str::FromStr for ApiExtensionId {
286 type Err = Self;
287
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 match s.parse::<u32>() {
290 Ok(i) => {
291 let r = Self::from_index(i as _);
292 if r == Self::INVALID { Err(r) } else { Ok(r) }
293 }
294 Err(_) => Err(Self::INVALID),
295 }
296 }
297}
298
299#[derive(Debug)]
301pub enum ApiExtensionRecvError {
302 UnknownExtension {
304 extension_id: ApiExtensionId,
308 },
309 InvalidRequest {
311 extension_id: ApiExtensionId,
315 error: Txt,
317 },
318 Deserialize(bincode::Error),
320}
321impl fmt::Display for ApiExtensionRecvError {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 match self {
324 ApiExtensionRecvError::UnknownExtension { extension_id } => write!(f, "invalid API request for unknown id {extension_id:?}"),
325 ApiExtensionRecvError::InvalidRequest { extension_id, error } => {
326 write!(f, "invalid API request for extension id {extension_id:?}, {error}")
327 }
328 ApiExtensionRecvError::Deserialize(e) => write!(f, "API extension response failed to deserialize, {e}"),
329 }
330 }
331}
332impl std::error::Error for ApiExtensionRecvError {
333 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
334 if let Self::Deserialize(e) = self { Some(e) } else { None }
335 }
336}