Skip to main content

ds_rom/
str.rs

1use std::{fmt::Display, str::FromStr};
2
3use bytemuck::{Pod, Zeroable};
4use serde::{de, Deserialize, Serialize};
5use snafu::{Backtrace, Snafu};
6
7/// A fixed-size ASCII string.
8#[derive(Clone, Copy)]
9pub struct AsciiArray<const N: usize>(pub [u8; N]);
10
11/// Errors related to [`AsciiArray`].
12#[derive(Debug, Snafu)]
13pub enum AsciiArrayError {
14    /// Occurs when an input character is not in ASCII.
15    #[snafu(display("the provided string '{string}' contains one or more non-ASCII characters:\n{backtrace}"))]
16    NotAscii {
17        /// The invalid string.
18        string: String,
19        /// Backtrace to the source of the error.
20        backtrace: Backtrace,
21    },
22}
23
24impl<const N: usize> FromStr for AsciiArray<N> {
25    type Err = AsciiArrayError;
26
27    /// Loads from a `&str`.
28    ///
29    /// # Errors
30    ///
31    /// This function will return an error if the string contains a non-ASCII character.
32    fn from_str(s: &str) -> Result<Self, Self::Err> {
33        let mut chars = [0u8; N];
34        for (i, ch) in s.chars().take(N).enumerate() {
35            if !ch.is_ascii() {
36                return NotAsciiSnafu { string: s.to_string() }.fail();
37            }
38            chars[i] = ch as u8;
39        }
40        Ok(Self(chars))
41    }
42}
43
44impl<const N: usize> Default for AsciiArray<N> {
45    fn default() -> Self {
46        Self([0; N])
47    }
48}
49
50impl AsciiArray<4> {
51    /// Converts a four-character ASCII string to a `u32`.
52    pub fn to_le_u32(&self) -> u32 {
53        u32::from_le_bytes(self.0)
54    }
55}
56
57impl<const N: usize> Display for AsciiArray<N> {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        for ch in self.0 {
60            if ch == 0 {
61                break;
62            }
63            write!(f, "{}", ch as char)?;
64        }
65        Ok(())
66    }
67}
68
69impl<const N: usize> Serialize for AsciiArray<N> {
70    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71    where
72        S: serde::Serializer,
73    {
74        serializer.serialize_str(&self.to_string())
75    }
76}
77
78impl<'de, const N: usize> Deserialize<'de> for AsciiArray<N> {
79    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80    where
81        D: serde::Deserializer<'de>,
82    {
83        let string: String = Deserialize::deserialize(deserializer)?;
84        Self::from_str(&string).map_err(de::Error::custom)
85    }
86}
87
88/// A fixed-size 16-bit Unicode string.
89#[derive(Clone, Copy)]
90pub struct Unicode16Array<const N: usize>(pub [u16; N]);
91
92unsafe impl<const N: usize> Zeroable for Unicode16Array<N> {}
93unsafe impl<const N: usize> Pod for Unicode16Array<N> {}
94
95impl<const N: usize> From<&str> for Unicode16Array<N> {
96    fn from(string: &str) -> Self {
97        let mut chars = [0u16; N];
98        let mut i = 0;
99        for ch in string.chars() {
100            let mut codepoints = [0u16; 2];
101            ch.encode_utf16(&mut codepoints);
102
103            let len = if codepoints[1] != 0 { 2 } else { 1 };
104            if i + len >= N {
105                break;
106            }
107
108            for j in 0..len {
109                chars[i] = codepoints[j];
110                i += 1;
111            }
112        }
113        Self(chars)
114    }
115}
116
117impl<const N: usize> Display for Unicode16Array<N> {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        for ch in self.0 {
120            if ch == 0 {
121                break;
122            }
123            let Some(ch) = char::from_u32(ch as u32) else {
124                break;
125            };
126            write!(f, "{ch}")?;
127        }
128        Ok(())
129    }
130}
131
132pub(crate) struct BlobSize(pub usize);
133
134impl Display for BlobSize {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        let size = self.0;
137        match size {
138            0..=0x3ff => write!(f, "{}B", size),
139            0x400..=0xfffff => write!(f, "{:.1}kB", size as f32 / 0x400 as f32),
140            0x100000.. => write!(f, "{:.1}MB", size as f32 / 0x100000 as f32),
141        }
142    }
143}