ab_io_type/
fixed_capacity_string.rs

1use crate::fixed_capacity_bytes::{FixedCapacityBytesU8, FixedCapacityBytesU16};
2use crate::metadata::{IoTypeMetadataKind, MAX_METADATA_CAPACITY, concat_metadata_sources};
3use crate::trivial_type::TrivialType;
4use core::ops::{Deref, DerefMut};
5
6/// Container for storing a UTF-8 string limited by the specified fixed bytes capacity as `u8`.
7///
8/// This is a string only by convention, there is no runtime verification done, contents is
9/// treated as regular bytes.
10///
11/// See also [`FixedCapacityStringU16`] if you need to store more bytes.
12///
13/// This is just a wrapper for [`FixedCapacityBytesU8`] that the type dereferences to with a
14/// different semantic meaning.
15#[derive(Debug, Copy, Clone)]
16#[repr(C)]
17pub struct FixedCapacityStringU8<const CAPACITY: usize> {
18    bytes: FixedCapacityBytesU8<CAPACITY>,
19}
20
21impl<const CAPACITY: usize> Default for FixedCapacityStringU8<CAPACITY> {
22    #[inline(always)]
23    fn default() -> Self {
24        Self {
25            bytes: FixedCapacityBytesU8::default(),
26        }
27    }
28}
29
30impl<const CAPACITY: usize> Deref for FixedCapacityStringU8<CAPACITY> {
31    type Target = FixedCapacityBytesU8<CAPACITY>;
32
33    fn deref(&self) -> &Self::Target {
34        &self.bytes
35    }
36}
37
38impl<const CAPACITY: usize> DerefMut for FixedCapacityStringU8<CAPACITY> {
39    fn deref_mut(&mut self) -> &mut Self::Target {
40        &mut self.bytes
41    }
42}
43
44// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
45unsafe impl<const CAPACITY: usize> TrivialType for FixedCapacityStringU8<CAPACITY> {
46    const METADATA: &[u8] = {
47        #[inline(always)]
48        const fn metadata(capacity: usize) -> ([u8; MAX_METADATA_CAPACITY], usize) {
49            assert!(
50                capacity <= u8::MAX as usize,
51                "`FixedCapacityStringU8` capacity must not exceed `u8::MAX`"
52            );
53            concat_metadata_sources(&[&[
54                IoTypeMetadataKind::FixedCapacityString8b as u8,
55                capacity as u8,
56            ]])
57        }
58        metadata(CAPACITY).0.split_at(metadata(CAPACITY).1).0
59    };
60}
61
62impl<const CAPACITY: usize> FixedCapacityStringU8<CAPACITY> {
63    /// Try to create an instance from provided string.
64    ///
65    /// Returns `None` if provided string does not fit into the capacity.
66    #[inline(always)]
67    pub fn try_from_str(s: &str) -> Option<Self> {
68        Self::try_from_bytes(s.as_bytes())
69    }
70
71    /// Try to create an instance from provided bytes.
72    ///
73    /// Returns `None` if provided bytes do not fit into the capacity.
74    #[inline(always)]
75    pub fn try_from_bytes(bytes: &[u8]) -> Option<Self> {
76        Some(Self {
77            bytes: FixedCapacityBytesU8::try_from_bytes(bytes)?,
78        })
79    }
80}
81
82/// Container for storing a UTF-8 string limited by the specified fixed bytes capacity as `u16`.
83///
84/// This is a string only by convention, there is no runtime verification done, contents is
85/// treated as regular bytes.
86///
87/// See also [`FixedCapacityStringU8`] if you need to store fewer bytes.
88///
89/// This is just a wrapper for [`FixedCapacityBytesU16`] that the type dereferences to with a
90/// different semantic meaning.
91#[derive(Debug, Copy, Clone)]
92#[repr(C)]
93pub struct FixedCapacityStringU16<const CAPACITY: usize> {
94    bytes: FixedCapacityBytesU16<CAPACITY>,
95}
96
97impl<const CAPACITY: usize> Default for FixedCapacityStringU16<CAPACITY> {
98    #[inline(always)]
99    fn default() -> Self {
100        Self {
101            bytes: FixedCapacityBytesU16::default(),
102        }
103    }
104}
105
106impl<const CAPACITY: usize> Deref for FixedCapacityStringU16<CAPACITY> {
107    type Target = FixedCapacityBytesU16<CAPACITY>;
108
109    fn deref(&self) -> &Self::Target {
110        &self.bytes
111    }
112}
113
114impl<const CAPACITY: usize> DerefMut for FixedCapacityStringU16<CAPACITY> {
115    fn deref_mut(&mut self) -> &mut Self::Target {
116        &mut self.bytes
117    }
118}
119
120// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
121unsafe impl<const CAPACITY: usize> TrivialType for FixedCapacityStringU16<CAPACITY> {
122    const METADATA: &[u8] = {
123        #[inline(always)]
124        const fn metadata(capacity: usize) -> ([u8; MAX_METADATA_CAPACITY], usize) {
125            assert!(
126                capacity <= u16::MAX as usize,
127                "`FixedCapacityStringU16` capacity must not exceed `u16::MAX`"
128            );
129            concat_metadata_sources(&[
130                &[IoTypeMetadataKind::FixedCapacityString16b as u8],
131                &(capacity as u16).to_le_bytes(),
132            ])
133        }
134        metadata(CAPACITY).0.split_at(metadata(CAPACITY).1).0
135    };
136}
137
138impl<const CAPACITY: usize> FixedCapacityStringU16<CAPACITY> {
139    /// Try to create an instance from provided string.
140    ///
141    /// Returns `None` if provided string does not fit into the capacity.
142    #[inline(always)]
143    pub fn try_from_str(s: &str) -> Option<Self> {
144        Self::try_from_bytes(s.as_bytes())
145    }
146
147    /// Try to create an instance from provided bytes.
148    ///
149    /// Returns `None` if provided bytes do not fit into the capacity.
150    #[inline(always)]
151    pub fn try_from_bytes(bytes: &[u8]) -> Option<Self> {
152        Some(Self {
153            bytes: FixedCapacityBytesU16::try_from_bytes(bytes)?,
154        })
155    }
156}