miden_mast_package/package/
section.rs1#[cfg(feature = "arbitrary")]
2use alloc::vec;
3use alloc::{
4 borrow::{Cow, ToOwned},
5 format,
6 string::ToString,
7};
8use core::{fmt, str::FromStr};
9
10use miden_assembly_syntax::DisplayHex;
11use miden_core::serde::{
12 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
13};
14#[cfg(feature = "arbitrary")]
15use proptest::prelude::*;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22#[cfg_attr(feature = "serde", serde(transparent))]
23#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
24#[repr(transparent)]
25pub struct SectionId(Cow<'static, str>);
26
27impl SectionId {
28 pub const DEBUG_TYPES: Self = Self(Cow::Borrowed("debug_types"));
31 pub const DEBUG_SOURCES: Self = Self(Cow::Borrowed("debug_sources"));
33 pub const DEBUG_FUNCTIONS: Self = Self(Cow::Borrowed("debug_functions"));
35 pub const DEBUG_SOURCE_GRAPH: Self = Self(Cow::Borrowed("debug_source_graph"));
37 pub const DEBUG_SOURCE_MAP: Self = Self(Cow::Borrowed("debug_source_map"));
39 pub const DEBUG_ERROR_MESSAGES: Self = Self(Cow::Borrowed("debug_error_messages"));
41 pub const ACCOUNT_COMPONENT_METADATA: Self = Self(Cow::Borrowed("account_component_metadata"));
47 pub const PROJECT_SOURCE_PROVENANCE: Self = Self(Cow::Borrowed("project_source_provenance"));
49 pub const KERNEL: Self = Self(Cow::Borrowed("kernel"));
51
52 pub fn custom(name: impl AsRef<str>) -> Result<Self, InvalidSectionIdError> {
58 let name = name.as_ref();
59 if !name.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_') {
60 return Err(InvalidSectionIdError::InvalidStart);
61 }
62 if name.contains(|c: char| !c.is_ascii_alphanumeric() && !matches!(c, '.' | '_' | '-')) {
63 return Err(InvalidSectionIdError::InvalidCharacter);
64 }
65 Ok(Self(name.to_string().into()))
66 }
67
68 #[inline]
70 pub fn as_str(&self) -> &str {
71 self.0.as_ref()
72 }
73
74 pub fn is_debug(&self) -> bool {
76 matches!(
77 self.as_str(),
78 "debug_types"
79 | "debug_sources"
80 | "debug_functions"
81 | "debug_source_graph"
82 | "debug_source_map"
83 | "debug_error_messages"
84 )
85 }
86}
87
88#[derive(Debug, thiserror::Error)]
89pub enum InvalidSectionIdError {
90 #[error("invalid section id: cannot be empty")]
91 Empty,
92 #[error(
93 "invalid section id: contains invalid characters, only the set [a-z0-9._-] are allowed"
94 )]
95 InvalidCharacter,
96 #[error("invalid section id: must start with a character in the set [a-z_]")]
97 InvalidStart,
98}
99
100impl FromStr for SectionId {
101 type Err = InvalidSectionIdError;
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 match s {
104 "debug_types" => Ok(Self::DEBUG_TYPES),
105 "debug_sources" => Ok(Self::DEBUG_SOURCES),
106 "debug_functions" => Ok(Self::DEBUG_FUNCTIONS),
107 "debug_source_graph" => Ok(Self::DEBUG_SOURCE_GRAPH),
108 "debug_source_map" => Ok(Self::DEBUG_SOURCE_MAP),
109 "debug_error_messages" => Ok(Self::DEBUG_ERROR_MESSAGES),
110 "account_component_metadata" => Ok(Self::ACCOUNT_COMPONENT_METADATA),
111 "project_source_provenance" => Ok(Self::PROJECT_SOURCE_PROVENANCE),
112 "kernel" => Ok(Self::KERNEL),
113 custom => Self::custom(custom),
114 }
115 }
116}
117
118impl fmt::Display for SectionId {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.write_str(self.as_str())
121 }
122}
123
124#[derive(Clone, PartialEq, Eq)]
125#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
126pub struct Section {
127 pub id: SectionId,
128 pub data: Cow<'static, [u8]>,
129}
130
131impl fmt::Debug for Section {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 let verbose = f.alternate();
134 let mut builder = f.debug_struct("Section");
135 builder.field("id", &format_args!("{}", self.id));
136 if verbose {
137 builder.field("data", &format_args!("{}", DisplayHex(&self.data))).finish()
138 } else {
139 builder.field("data", &format_args!("{} bytes", self.data.len())).finish()
140 }
141 }
142}
143
144impl Section {
145 pub fn new<B>(id: SectionId, data: B) -> Self
146 where
147 B: Into<Cow<'static, [u8]>>,
148 {
149 Self { id, data: data.into() }
150 }
151
152 pub fn is_empty(&self) -> bool {
154 self.data.is_empty()
155 }
156
157 pub fn len(&self) -> usize {
159 self.data.len()
160 }
161}
162
163impl Serializable for Section {
164 fn write_into<W: ByteWriter>(&self, target: &mut W) {
165 let id = self.id.as_str();
166 target.write_usize(id.len());
167 target.write_bytes(id.as_bytes());
168 target.write_usize(self.len());
169 target.write_bytes(&self.data);
170 }
171}
172
173impl Deserializable for Section {
174 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
175 let id_len = source.read_usize()?;
176 let id_bytes = source.read_slice(id_len)?;
177 let id_str = core::str::from_utf8(id_bytes).map_err(|err| {
178 DeserializationError::InvalidValue(format!("invalid utf-8 in section name: {err}"))
179 })?;
180 let id = SectionId(Cow::Owned(id_str.to_owned()));
181
182 let len = source.read_usize()?;
183 let bytes = source.read_slice(len)?;
184 Ok(Section { id, data: Cow::Owned(bytes.to_owned()) })
185 }
186}
187
188#[cfg(feature = "arbitrary")]
189impl Arbitrary for SectionId {
190 type Parameters = ();
191 type Strategy = BoxedStrategy<Self>;
192
193 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
194 use alloc::string::String;
195
196 let builtins = proptest::sample::select(vec![
197 Self::DEBUG_TYPES,
198 Self::DEBUG_SOURCES,
199 Self::DEBUG_FUNCTIONS,
200 Self::DEBUG_SOURCE_GRAPH,
201 Self::DEBUG_SOURCE_MAP,
202 Self::DEBUG_ERROR_MESSAGES,
203 Self::ACCOUNT_COMPONENT_METADATA,
204 Self::PROJECT_SOURCE_PROVENANCE,
205 Self::KERNEL,
206 ]);
207
208 let custom = (
209 proptest::prop_oneof![
210 proptest::char::range('a', 'z'),
211 proptest::char::range('A', 'Z'),
212 Just('_'),
213 ],
214 proptest::collection::vec(
215 proptest::prop_oneof![
216 proptest::char::range('a', 'z'),
217 proptest::char::range('A', 'Z'),
218 proptest::char::range('0', '9'),
219 Just('.'),
220 Just('_'),
221 Just('-'),
222 ],
223 0..31,
224 ),
225 )
226 .prop_map(|(first, rest)| {
227 let mut name = String::with_capacity(rest.len() + 1);
228 name.push(first);
229 name.extend(rest);
230 Self::custom(name).expect("generated custom section ids are valid")
231 });
232
233 proptest::prop_oneof![builtins, custom].boxed()
234 }
235}