marine_module_info_parser/manifest/
module_manifest.rs1#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct ModuleManifest {
20 pub authors: String,
21 pub version: semver::Version,
22 pub description: String,
23 pub repository: String,
24 pub build_time: chrono::DateTime<chrono::FixedOffset>,
25}
26
27use super::ManifestError;
28
29use std::convert::TryFrom;
30use std::str::FromStr;
31
32type Result<T> = std::result::Result<T, ManifestError>;
33
34impl TryFrom<&[u8]> for ModuleManifest {
35 type Error = ManifestError;
36
37 #[rustfmt::skip]
38 fn try_from(value: &[u8]) -> Result<Self> {
39 let (authors, next_offset) = try_extract_field_as_string(value, 0, "authors")?;
40 let (version, next_offset) = try_extract_field_as_version(value, next_offset, "version")?;
41 let (description, next_offset) = try_extract_field_as_string(value, next_offset, "description")?;
42 let (repository, next_offset) = try_extract_field_as_string(value, next_offset, "repository")?;
43 let (build_time, next_offset) = try_extract_field_as_string(value, next_offset, "build time")?;
44
45 if next_offset != value.len() {
46 return Err(ManifestError::ManifestRemainderNotEmpty)
47 }
48
49 let build_time = chrono::DateTime::parse_from_rfc3339(&build_time)?;
50
51 let manifest = ModuleManifest {
52 authors,
53 version,
54 description,
55 repository,
56 build_time
57 };
58
59 Ok(manifest)
60 }
61}
62
63fn try_extract_field_as_string(
64 raw_manifest: &[u8],
65 offset: usize,
66 field_name: &'static str,
67) -> Result<(String, usize)> {
68 let raw_manifest = &raw_manifest[offset..];
69 let (field_as_bytes, read_len) = try_extract_prefixed_field(raw_manifest, field_name)?;
70 let field_as_string = try_to_str(field_as_bytes, field_name)?.to_string();
71
72 Ok((field_as_string, offset + read_len))
73}
74
75fn try_extract_field_as_version(
76 raw_manifest: &[u8],
77 offset: usize,
78 field_name: &'static str,
79) -> Result<(semver::Version, usize)> {
80 let raw_manifest = &raw_manifest[offset..];
81 let (field_as_bytes, read_len) = try_extract_prefixed_field(raw_manifest, field_name)?;
82 let field_as_str = try_to_str(field_as_bytes, field_name)?;
83 let version = semver::Version::from_str(field_as_str)?;
84
85 Ok((version, offset + read_len))
86}
87
88const PREFIX_SIZE: usize = std::mem::size_of::<u64>();
89
90fn try_extract_prefixed_field<'a>(
91 array: &'a [u8],
92 field_name: &'static str,
93) -> Result<(&'a [u8], usize)> {
94 let field_len = try_extract_field_len(array, field_name)?;
95 let field = try_extract_field(array, field_len, field_name)?;
96
97 let read_size = PREFIX_SIZE + field.len();
98 Ok((field, read_size))
99}
100
101fn try_extract_field_len(array: &[u8], field_name: &'static str) -> Result<usize> {
102 if array.len() < PREFIX_SIZE {
103 return Err(ManifestError::NotEnoughBytesForPrefix(field_name));
104 }
105
106 let mut field_len = [0u8; PREFIX_SIZE];
107 field_len.copy_from_slice(&array[0..PREFIX_SIZE]);
108
109 let field_len = u64::from_le_bytes(field_len);
110 if field_len.checked_add(PREFIX_SIZE as u64).is_none()
112 || usize::try_from(field_len + PREFIX_SIZE as u64).is_err()
113 {
114 return Err(ManifestError::TooBigFieldSize(field_name, field_len));
115 }
116
117 Ok(field_len as usize)
119}
120
121fn try_extract_field<'a>(
122 array: &'a [u8],
123 field_len: usize,
124 field_name: &'static str,
125) -> Result<&'a [u8]> {
126 if array.len() < PREFIX_SIZE + field_len {
127 return Err(ManifestError::NotEnoughBytesForField(field_name, field_len));
128 }
129
130 let field = &array[PREFIX_SIZE..PREFIX_SIZE + field_len];
131 Ok(field)
132}
133
134fn try_to_str<'v>(value: &'v [u8], field_name: &'static str) -> Result<&'v str> {
135 match std::str::from_utf8(value) {
136 Ok(s) => Ok(s),
137 Err(e) => Err(ManifestError::FieldNotValidUtf8(field_name, e)),
138 }
139}
140
141use std::fmt;
142
143impl fmt::Display for ModuleManifest {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 writeln!(f, "authors: {}", self.authors)?;
146 writeln!(f, "version: {}", self.version)?;
147 writeln!(f, "description: {}", self.description)?;
148 writeln!(f, "repository: {}", self.repository)?;
149 write!(f, "build time: {} UTC", self.build_time)
150 }
151}