1use std::{fmt::Display, str::FromStr};
2
3use bytemuck::{Pod, Zeroable};
4use serde::{de, Deserialize, Serialize};
5use snafu::{Backtrace, Snafu};
6
7#[derive(Clone, Copy)]
9pub struct AsciiArray<const N: usize>(pub [u8; N]);
10
11#[derive(Debug, Snafu)]
13pub enum AsciiArrayError {
14 #[snafu(display("the provided string '{string}' contains one or more non-ASCII characters:\n{backtrace}"))]
16 NotAscii {
17 string: String,
19 backtrace: Backtrace,
21 },
22}
23
24impl<const N: usize> FromStr for AsciiArray<N> {
25 type Err = AsciiArrayError;
26
27 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 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#[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}