vapoursynth4_rs/map/
key.rs

1use std::{
2    ffi::{CStr, CString, c_char},
3    fmt::{Debug, Display},
4    ops::Deref,
5    ptr,
6};
7
8use thiserror::Error;
9
10#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
11#[repr(transparent)]
12pub struct Key {
13    inner: CString,
14}
15
16impl Key {
17    /// # Errors
18    ///
19    /// Return [`InvalidKey`] if the key contains characters that are not alphanumeric
20    /// or underscore
21    pub fn new<T>(val: T) -> Result<Self, InvalidKey>
22    where
23        T: Into<Vec<u8>>,
24    {
25        let mut val: Vec<u8> = val.into();
26        if let Some(i) = val.iter().position(|&c| c == 0) {
27            val.drain(i..);
28        }
29        if val.iter().all(|&c| c.is_ascii_alphanumeric() || c == b'_') {
30            val.push(0);
31            Ok(Self {
32                inner: unsafe { CString::from_vec_with_nul_unchecked(val) },
33            })
34        } else {
35            Err(InvalidKey)
36        }
37    }
38}
39
40impl Deref for Key {
41    type Target = KeyStr;
42
43    fn deref(&self) -> &Self::Target {
44        // SAFETY: Key is validated
45        unsafe { KeyStr::from_cstr_unchecked(self.inner.as_c_str()) }
46    }
47}
48
49impl From<&KeyStr> for Key {
50    fn from(value: &KeyStr) -> Self {
51        Self {
52            inner: value.inner.into(),
53        }
54    }
55}
56
57impl Display for Key {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        // SAFETY: Key is validated
60        unsafe { f.write_str(std::str::from_utf8_unchecked(self.inner.as_bytes())) }
61    }
62}
63
64#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
65#[repr(transparent)]
66pub struct KeyStr {
67    inner: CStr,
68}
69
70impl KeyStr {
71    #[must_use]
72    pub const fn from_cstr(str: &CStr) -> &Self {
73        let mut i = 0;
74        let slice = str.to_bytes();
75        while i < slice.len() {
76            let c = slice[i];
77            assert!(
78                c.is_ascii_alphanumeric() || c == b'_',
79                "Key must be alphanumeric or underscore"
80            );
81            i += 1;
82        }
83        unsafe { Self::from_cstr_unchecked(str) }
84    }
85
86    #[must_use]
87    /// # Safety
88    /// The caller must ensure that the key is valid to contain only characters that are alphanumeric or underscore
89    pub const unsafe fn from_cstr_unchecked(str: &CStr) -> &Self {
90        unsafe { &*(ptr::from_ref(str) as *const KeyStr) }
91    }
92
93    pub(crate) unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
94        Self::from_cstr(unsafe { CStr::from_ptr(ptr) })
95    }
96}
97
98impl Deref for KeyStr {
99    type Target = CStr;
100
101    fn deref(&self) -> &Self::Target {
102        &self.inner
103    }
104}
105
106impl Display for KeyStr {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        unsafe { f.write_str(std::str::from_utf8_unchecked(self.inner.to_bytes())) }
109    }
110}
111
112#[macro_export]
113macro_rules! key {
114    ($s:expr) => {
115        const { $crate::map::KeyStr::from_cstr($s) }
116    };
117}
118
119#[derive(Debug, Error)]
120#[error("Key is invalid. Only ascii alphanumeric or underscore is allowed.")]
121pub struct InvalidKey;