miden_mast_package/package/
section.rs1use alloc::{
2 borrow::{Cow, ToOwned},
3 format,
4 string::ToString,
5};
6use core::{fmt, str::FromStr};
7
8use miden_assembly_syntax::DisplayHex;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15#[cfg_attr(feature = "serde", serde(transparent))]
16#[repr(transparent)]
17pub struct SectionId(Cow<'static, str>);
18
19impl SectionId {
20 pub const DEBUG_INFO: Self = Self(Cow::Borrowed("debug_info"));
22 pub const ACCOUNT_COMPONENT_METADATA: Self = Self(Cow::Borrowed("account_component_metadata"));
28
29 pub fn custom(name: impl AsRef<str>) -> Result<Self, InvalidSectionIdError> {
35 let name = name.as_ref();
36 if !name.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_') {
37 return Err(InvalidSectionIdError::InvalidStart);
38 }
39 if name.contains(|c: char| !c.is_ascii_alphanumeric() && !matches!(c, '.' | '_' | '-')) {
40 return Err(InvalidSectionIdError::InvalidCharacter);
41 }
42 Ok(Self(name.to_string().into()))
43 }
44
45 #[inline]
47 pub fn as_str(&self) -> &str {
48 self.0.as_ref()
49 }
50}
51
52#[derive(Debug, thiserror::Error)]
53pub enum InvalidSectionIdError {
54 #[error("invalid section id: cannot be empty")]
55 Empty,
56 #[error(
57 "invalid section id: contains invalid characters, only the set [a-z0-9._-] are allowed"
58 )]
59 InvalidCharacter,
60 #[error("invalid section id: must start with a character in the set [a-z_]")]
61 InvalidStart,
62}
63
64impl FromStr for SectionId {
65 type Err = InvalidSectionIdError;
66 fn from_str(s: &str) -> Result<Self, Self::Err> {
67 match s {
68 "debug_info" => Ok(Self::DEBUG_INFO),
69 "account_component_metadata" => Ok(Self::ACCOUNT_COMPONENT_METADATA),
70 custom => Self::custom(custom),
71 }
72 }
73}
74
75impl fmt::Display for SectionId {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 f.write_str(self.as_str())
78 }
79}
80
81#[derive(Clone, PartialEq, Eq)]
82#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
83pub struct Section {
84 pub id: SectionId,
85 pub data: Cow<'static, [u8]>,
86}
87
88impl fmt::Debug for Section {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 let verbose = f.alternate();
91 let mut builder = f.debug_struct("Section");
92 builder.field("id", &format_args!("{}", &self.id));
93 if verbose {
94 builder.field("data", &format_args!("{}", DisplayHex(&self.data))).finish()
95 } else {
96 builder.field("data", &format_args!("{} bytes", self.data.len())).finish()
97 }
98 }
99}
100
101impl Section {
102 pub fn new<B>(id: SectionId, data: B) -> Self
103 where
104 B: Into<Cow<'static, [u8]>>,
105 {
106 Self { id, data: data.into() }
107 }
108
109 pub fn is_empty(&self) -> bool {
111 self.data.is_empty()
112 }
113
114 pub fn len(&self) -> usize {
116 self.data.len()
117 }
118}
119
120impl miden_core::utils::Serializable for Section {
121 fn write_into<W: miden_core::utils::ByteWriter>(&self, target: &mut W) {
122 let id = self.id.as_str();
123 target.write_usize(id.len());
124 target.write_bytes(id.as_bytes());
125 target.write_usize(self.len());
126 target.write_bytes(&self.data);
127 }
128}
129
130impl miden_core::utils::Deserializable for Section {
131 fn read_from<R: miden_core::utils::ByteReader>(
132 source: &mut R,
133 ) -> Result<Self, miden_core::utils::DeserializationError> {
134 let id_len = source.read_usize()?;
135 let id_bytes = source.read_slice(id_len)?;
136 let id_str = core::str::from_utf8(id_bytes).map_err(|err| {
137 miden_core::utils::DeserializationError::InvalidValue(format!(
138 "invalid utf-8 in section name: {err}"
139 ))
140 })?;
141 let id = SectionId(Cow::Owned(id_str.to_owned()));
142
143 let len = source.read_usize()?;
144 let bytes = source.read_slice(len)?;
145 Ok(Section { id, data: Cow::Owned(bytes.to_owned()) })
146 }
147}