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 if let Ok(id_str) = std::str::from_utf8(p) {
70 return match id_str.parse::<ApiExtensionId>() {
71 Ok(id) => Some(id),
72 Err(id) => Some(id),
73 };
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 if let Some(id_end) = p.iter().position(|&b| b == b';') {
89 if let Ok(id_str) = std::str::from_utf8(&p[..id_end]) {
90 let id = match id_str.parse::<ApiExtensionId>() {
91 Ok(id) => id,
92 Err(id) => id,
93 };
94 if let Some(p) = p[id_end..].strip_prefix(b";error=") {
95 if let Ok(err_str) = std::str::from_utf8(p) {
96 return Some((id, err_str));
97 }
98 }
99 return Some((id, "invalid request, corrupted payload, unknown error"));
100 }
101 }
102 }
103 Some((
104 ApiExtensionId::INVALID,
105 "invalid request, corrupted payload, unknown extension_id and error",
106 ))
107 }
108}
109impl fmt::Debug for ApiExtensionPayload {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "ExtensionPayload({} bytes)", self.0.len())
112 }
113}
114
115#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
122pub struct ApiExtensionName {
123 name: Txt,
124}
125impl ApiExtensionName {
126 pub fn new(name: impl Into<Txt>) -> Result<Self, ApiExtensionNameError> {
130 let name = name.into();
131 Self::new_impl(name)
132 }
133 fn new_impl(name: Txt) -> Result<ApiExtensionName, ApiExtensionNameError> {
134 if name.is_empty() {
135 return Err(ApiExtensionNameError::NameCannotBeEmpty);
136 }
137 for (i, c) in name.char_indices() {
138 if i == 0 {
139 if !c.is_ascii_alphabetic() {
140 return Err(ApiExtensionNameError::NameCannotStartWithChar(c));
141 }
142 } else if !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.' {
143 return Err(ApiExtensionNameError::NameInvalidChar(c));
144 }
145 }
146
147 Ok(Self { name })
148 }
149}
150impl fmt::Debug for ApiExtensionName {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 fmt::Debug::fmt(&self.name, f)
153 }
154}
155impl fmt::Display for ApiExtensionName {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 fmt::Display::fmt(&self.name, f)
158 }
159}
160impl ops::Deref for ApiExtensionName {
161 type Target = str;
162
163 fn deref(&self) -> &Self::Target {
164 self.name.as_str()
165 }
166}
167impl From<&'static str> for ApiExtensionName {
168 fn from(value: &'static str) -> Self {
169 Self::new(value).unwrap()
170 }
171}
172
173#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
175#[non_exhaustive]
176pub enum ApiExtensionNameError {
177 NameCannotBeEmpty,
179 NameCannotStartWithChar(char),
181 NameInvalidChar(char),
183}
184impl fmt::Display for ApiExtensionNameError {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 match self {
187 ApiExtensionNameError::NameCannotBeEmpty => write!(f, "API extension name cannot be empty"),
188 ApiExtensionNameError::NameCannotStartWithChar(c) => {
189 write!(f, "API cannot start with '{c}', name pattern `[a-zA-Z][a-zA-Z0-9-_.]`")
190 }
191 ApiExtensionNameError::NameInvalidChar(c) => write!(f, "API cannot contain '{c}', name pattern `[a-zA-Z][a-zA-Z0-9-_.]`"),
192 }
193 }
194}
195impl std::error::Error for ApiExtensionNameError {}
196
197#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
199pub struct ApiExtensions(Vec<ApiExtensionName>);
200impl ops::Deref for ApiExtensions {
201 type Target = [ApiExtensionName];
202
203 fn deref(&self) -> &Self::Target {
204 &self.0
205 }
206}
207impl ApiExtensions {
208 pub fn new() -> Self {
210 Self::default()
211 }
212
213 pub fn id(&self, ext: &ApiExtensionName) -> Option<ApiExtensionId> {
222 self.0.iter().position(|e| e == ext).map(ApiExtensionId::from_index)
223 }
224
225 pub fn insert(&mut self, ext: ApiExtensionName) -> Result<ApiExtensionId, ApiExtensionId> {
229 if let Some(key) = self.id(&ext) {
230 Err(key)
231 } else {
232 let key = self.0.len();
233 self.0.push(ext);
234 Ok(ApiExtensionId::from_index(key))
235 }
236 }
237}
238
239#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
241#[serde(transparent)]
242pub struct ApiExtensionId(u32);
243impl fmt::Debug for ApiExtensionId {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 if *self == Self::INVALID {
246 if f.alternate() {
247 write!(f, "ApiExtensionId::")?;
248 }
249 write!(f, "INVALID")
250 } else {
251 write!(f, "ApiExtensionId({})", self.0 - 1)
252 }
253 }
254}
255impl fmt::Display for ApiExtensionId {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 if *self == Self::INVALID {
258 write!(f, "invalid")
259 } else {
260 write!(f, "{}", self.0 - 1)
261 }
262 }
263}
264impl ApiExtensionId {
265 pub const INVALID: Self = Self(0);
267
268 pub fn index(self) -> usize {
274 self.0.checked_sub(1).expect("invalid id") as _
275 }
276
277 pub fn from_index(idx: usize) -> Self {
283 if idx > (u32::MAX - 1) as _ {
284 panic!("index out-of-bounds")
285 }
286 Self(idx as u32 + 1)
287 }
288}
289impl std::str::FromStr for ApiExtensionId {
290 type Err = Self;
291
292 fn from_str(s: &str) -> Result<Self, Self::Err> {
293 match s.parse::<u32>() {
294 Ok(i) => {
295 let r = Self::from_index(i as _);
296 if r == Self::INVALID { Err(r) } else { Ok(r) }
297 }
298 Err(_) => Err(Self::INVALID),
299 }
300 }
301}
302
303#[derive(Debug)]
305#[non_exhaustive]
306pub enum ApiExtensionRecvError {
307 UnknownExtension {
309 extension_id: ApiExtensionId,
313 },
314 InvalidRequest {
316 extension_id: ApiExtensionId,
320 error: Txt,
322 },
323 Deserialize(bincode::error::DecodeError),
325}
326impl fmt::Display for ApiExtensionRecvError {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 match self {
329 ApiExtensionRecvError::UnknownExtension { extension_id } => write!(f, "invalid API request for unknown id {extension_id:?}"),
330 ApiExtensionRecvError::InvalidRequest { extension_id, error } => {
331 write!(f, "invalid API request for extension id {extension_id:?}, {error}")
332 }
333 ApiExtensionRecvError::Deserialize(e) => write!(f, "API extension response failed to deserialize, {e}"),
334 }
335 }
336}
337impl std::error::Error for ApiExtensionRecvError {
338 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
339 if let Self::Deserialize(e) = self { Some(e) } else { None }
340 }
341}