zenoh_protocol_core/
encoding.rs

1//
2// Copyright (c) 2022 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14use crate::ZInt;
15use core::fmt;
16use std::{borrow::Cow, convert::TryFrom};
17
18mod consts {
19    pub(super) const MIMES: [&str; 21] = [
20        /*  0 */ "",
21        /*  1 */ "application/octet-stream",
22        /*  2 */ "application/custom", // non iana standard
23        /*  3 */ "text/plain",
24        /*  4 */ "application/properties", // non iana standard
25        /*  5 */ "application/json", // if not readable from casual users
26        /*  6 */ "application/sql",
27        /*  7 */ "application/integer", // non iana standard
28        /*  8 */ "application/float", // non iana standard
29        /*  9 */
30        "application/xml", // if not readable from casual users (RFC 3023, sec 3)
31        /* 10 */ "application/xhtml+xml",
32        /* 11 */ "application/x-www-form-urlencoded",
33        /* 12 */ "text/json", // non iana standard - if readable from casual users
34        /* 13 */ "text/html",
35        /* 14 */ "text/xml", // if readable from casual users (RFC 3023, section 3)
36        /* 15 */ "text/css",
37        /* 16 */ "text/csv",
38        /* 17 */ "text/javascript",
39        /* 18 */ "image/jpeg",
40        /* 19 */ "image/png",
41        /* 20 */ "image/gif",
42    ];
43}
44
45#[repr(u8)]
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum KnownEncoding {
48    Empty = 0,
49    AppOctetStream = 1,
50    AppCustom = 2,
51    TextPlain = 3,
52    AppProperties = 4,
53    AppJson = 5,
54    AppSql = 6,
55    AppInteger = 7,
56    AppFloat = 8,
57    AppXml = 9,
58    AppXhtmlXml = 10,
59    AppXWwwFormUrlencoded = 11,
60    TextJson = 12,
61    TextHtml = 13,
62    TextXml = 14,
63    TextCss = 15,
64    TextCsv = 16,
65    TextJavascript = 17,
66    ImageJpeg = 18,
67    ImagePng = 19,
68    ImageGif = 20,
69}
70
71impl From<KnownEncoding> for u8 {
72    fn from(val: KnownEncoding) -> Self {
73        unsafe { std::mem::transmute(val) }
74    }
75}
76
77impl From<KnownEncoding> for &str {
78    fn from(val: KnownEncoding) -> Self {
79        consts::MIMES[usize::from(val)]
80    }
81}
82
83impl From<KnownEncoding> for usize {
84    fn from(val: KnownEncoding) -> Self {
85        u8::from(val) as usize
86    }
87}
88
89impl std::convert::TryFrom<u8> for KnownEncoding {
90    type Error = ();
91    fn try_from(value: u8) -> Result<Self, Self::Error> {
92        if value < consts::MIMES.len() as u8 + 1 {
93            Ok(unsafe { std::mem::transmute(value) })
94        } else {
95            Err(())
96        }
97    }
98}
99
100impl std::convert::TryFrom<ZInt> for KnownEncoding {
101    type Error = ();
102
103    fn try_from(value: ZInt) -> Result<Self, Self::Error> {
104        if value < consts::MIMES.len() as ZInt + 1 {
105            Ok(unsafe { std::mem::transmute(value as u8) })
106        } else {
107            Err(())
108        }
109    }
110}
111
112impl AsRef<str> for KnownEncoding {
113    fn as_ref(&self) -> &str {
114        consts::MIMES[usize::from(*self)]
115    }
116}
117/// The encoding of a zenoh `zenoh::Value`.
118///
119/// A zenoh encoding is a HTTP Mime type represented, for wire efficiency,
120/// as an integer prefix (that maps to a string) and a string suffix.
121#[derive(Clone, Debug, PartialEq, Eq)]
122pub enum Encoding {
123    Exact(KnownEncoding),
124    WithSuffix(KnownEncoding, Cow<'static, str>),
125}
126
127impl Encoding {
128    pub fn new<IntoCowStr>(prefix: ZInt, suffix: IntoCowStr) -> Option<Self>
129    where
130        IntoCowStr: Into<Cow<'static, str>> + AsRef<str>,
131    {
132        let prefix = KnownEncoding::try_from(prefix).ok()?;
133        if suffix.as_ref().is_empty() {
134            Some(Encoding::Exact(prefix))
135        } else {
136            Some(Encoding::WithSuffix(prefix, suffix.into()))
137        }
138    }
139
140    /// Sets the suffix of this encoding.
141    pub fn with_suffix<IntoCowStr>(self, suffix: IntoCowStr) -> Self
142    where
143        IntoCowStr: Into<Cow<'static, str>>,
144    {
145        match self {
146            Encoding::Exact(e) => Encoding::WithSuffix(e, suffix.into()),
147            Encoding::WithSuffix(e, s) => {
148                Encoding::WithSuffix(e, Cow::Owned(format!("{}{}", s, suffix.into())))
149            }
150        }
151    }
152
153    pub fn as_ref<'a, T>(&'a self) -> T
154    where
155        &'a Self: Into<T>,
156    {
157        self.into()
158    }
159
160    /// Returns `true`if the string representation of this encoding starts with
161    /// the string representation of ther given encoding.
162    pub fn starts_with<T>(&self, with: T) -> bool
163    where
164        T: Into<Encoding>,
165    {
166        let with: Encoding = with.into();
167        self.prefix() == with.prefix() && self.suffix().starts_with(with.suffix())
168    }
169
170    pub const fn prefix(&self) -> &KnownEncoding {
171        match self {
172            Encoding::Exact(e) | Encoding::WithSuffix(e, _) => e,
173        }
174    }
175
176    pub fn suffix(&self) -> &str {
177        match self {
178            Encoding::Exact(_) => "",
179            Encoding::WithSuffix(_, s) => s.as_ref(),
180        }
181    }
182}
183
184impl Encoding {
185    pub const EMPTY: Encoding = Encoding::Exact(KnownEncoding::Empty);
186    pub const APP_OCTET_STREAM: Encoding = Encoding::Exact(KnownEncoding::AppOctetStream);
187    pub const APP_CUSTOM: Encoding = Encoding::Exact(KnownEncoding::AppCustom);
188    pub const TEXT_PLAIN: Encoding = Encoding::Exact(KnownEncoding::TextPlain);
189    pub const APP_PROPERTIES: Encoding = Encoding::Exact(KnownEncoding::AppProperties);
190    pub const APP_JSON: Encoding = Encoding::Exact(KnownEncoding::AppJson);
191    pub const APP_SQL: Encoding = Encoding::Exact(KnownEncoding::AppSql);
192    pub const APP_INTEGER: Encoding = Encoding::Exact(KnownEncoding::AppInteger);
193    pub const APP_FLOAT: Encoding = Encoding::Exact(KnownEncoding::AppFloat);
194    pub const APP_XML: Encoding = Encoding::Exact(KnownEncoding::AppXml);
195    pub const APP_XHTML_XML: Encoding = Encoding::Exact(KnownEncoding::AppXhtmlXml);
196    pub const APP_XWWW_FORM_URLENCODED: Encoding =
197        Encoding::Exact(KnownEncoding::AppXWwwFormUrlencoded);
198    pub const TEXT_JSON: Encoding = Encoding::Exact(KnownEncoding::TextJson);
199    pub const TEXT_HTML: Encoding = Encoding::Exact(KnownEncoding::TextHtml);
200    pub const TEXT_XML: Encoding = Encoding::Exact(KnownEncoding::TextXml);
201    pub const TEXT_CSS: Encoding = Encoding::Exact(KnownEncoding::TextCss);
202    pub const TEXT_CSV: Encoding = Encoding::Exact(KnownEncoding::TextCsv);
203    pub const TEXT_JAVASCRIPT: Encoding = Encoding::Exact(KnownEncoding::TextJavascript);
204    pub const IMAGE_JPEG: Encoding = Encoding::Exact(KnownEncoding::ImageJpeg);
205    pub const IMAGE_PNG: Encoding = Encoding::Exact(KnownEncoding::ImagePng);
206    pub const IMAGE_GIF: Encoding = Encoding::Exact(KnownEncoding::ImageGif);
207}
208
209impl fmt::Display for Encoding {
210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        match self {
212            Encoding::Exact(e) => f.write_str(e.as_ref()),
213            Encoding::WithSuffix(e, s) => {
214                f.write_str(e.as_ref())?;
215                f.write_str(s)
216            }
217        }
218    }
219}
220
221impl From<&'static str> for Encoding {
222    fn from(s: &'static str) -> Self {
223        for (i, v) in consts::MIMES.iter().enumerate().skip(1) {
224            if let Some(suffix) = s.strip_prefix(v) {
225                if suffix.is_empty() {
226                    return Encoding::Exact(unsafe { std::mem::transmute(i as u8) });
227                } else {
228                    return Encoding::WithSuffix(
229                        unsafe { std::mem::transmute(i as u8) },
230                        suffix.into(),
231                    );
232                }
233            }
234        }
235        if s.is_empty() {
236            Encoding::Exact(KnownEncoding::Empty)
237        } else {
238            Encoding::WithSuffix(KnownEncoding::Empty, s.into())
239        }
240    }
241}
242
243impl From<String> for Encoding {
244    fn from(mut s: String) -> Self {
245        for (i, v) in consts::MIMES.iter().enumerate().skip(1) {
246            if s.starts_with(v) {
247                s.replace_range(..v.len(), "");
248                if s.is_empty() {
249                    return Encoding::Exact(unsafe { std::mem::transmute(i as u8) });
250                } else {
251                    return Encoding::WithSuffix(unsafe { std::mem::transmute(i as u8) }, s.into());
252                }
253            }
254        }
255        if s.is_empty() {
256            Encoding::Exact(KnownEncoding::Empty)
257        } else {
258            Encoding::WithSuffix(KnownEncoding::Empty, s.into())
259        }
260    }
261}
262
263impl From<&KnownEncoding> for Encoding {
264    fn from(e: &KnownEncoding) -> Encoding {
265        Encoding::Exact(*e)
266    }
267}
268
269impl From<KnownEncoding> for Encoding {
270    fn from(e: KnownEncoding) -> Encoding {
271        Encoding::Exact(e)
272    }
273}
274
275impl Default for Encoding {
276    fn default() -> Self {
277        KnownEncoding::Empty.into()
278    }
279}