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