1use serde::de::{Error, Unexpected};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use std::collections::HashMap;
9use std::fmt::{self, Debug, Display, Formatter};
10use std::ops::Deref;
11use tokio::io;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(untagged)]
16pub enum Entry {
17 File(FileMetadata),
19
20 Directory(Directory),
22}
23
24impl Entry {
25 pub(crate) fn search_segments(&self, segments: &[&str]) -> Option<&Entry> {
26 match self {
27 _ if segments.is_empty() => Some(self),
28 Self::File(_) => None,
29 Self::Directory(dir) => dir.search_segments(segments),
30 }
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct FileMetadata {
37 #[serde(flatten)]
39 pub pos: FilePosition,
40
41 pub size: u64,
49
50 #[serde(default)]
52 #[serde(skip_serializing_if = "bool::clone")]
53 pub executable: bool,
54
55 pub integrity: Option<Integrity>,
57}
58
59impl FileMetadata {
60 pub(crate) fn offset(&self) -> io::Result<u64> {
61 if let FilePosition::Offset(x) = self.pos {
62 Ok(x)
63 } else {
64 Err(io::Error::new(
65 io::ErrorKind::Other,
66 "unpacked file is currently not supported",
67 ))
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy)]
74pub enum FilePosition {
75 Offset(u64),
77
78 Unpacked,
80}
81
82#[derive(Serialize, Deserialize)]
83#[serde(untagged)]
84enum Helper<'a> {
85 Offset {
86 #[serde(skip_serializing_if = "Option::is_none")]
87 unpacked: Option<bool>,
88 offset: &'a str,
89 },
90 Unpacked {
91 unpacked: bool,
92 },
93}
94
95impl Serialize for FilePosition {
96 fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
97 let offset_string;
98 let helper = match self {
99 Self::Offset(offset) => {
100 offset_string = offset.to_string();
101 Helper::Offset {
102 unpacked: None,
103 offset: &offset_string,
104 }
105 }
106 Self::Unpacked => Helper::Unpacked { unpacked: true },
107 };
108
109 helper.serialize(ser)
110 }
111}
112
113impl<'de> Deserialize<'de> for FilePosition {
114 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
115 match Helper::deserialize(de)? {
116 Helper::Offset { unpacked, .. } if matches!(unpacked, Some(true)) => {
117 Err(Error::custom("got both 'unpacked' and 'offset' field"))
118 }
119 Helper::Offset { offset, .. } => offset
120 .parse()
121 .map(Self::Offset)
122 .map_err(|_| Error::invalid_value(Unexpected::Str(offset), &"valid u64 string")),
123 Helper::Unpacked { unpacked: true } => Ok(Self::Unpacked),
124 Helper::Unpacked { unpacked: false } => {
125 Err(Error::invalid_value(Unexpected::Bool(false), &"true"))
126 }
127 }
128 }
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct Integrity {
134 pub algorithm: Algorithm,
136
137 pub hash: Hash,
139
140 #[serde(rename = "blockSize")]
142 pub block_size: u32,
143
144 pub blocks: Vec<Hash>,
146}
147
148#[derive(Clone, Serialize, Deserialize)]
149pub struct Hash(#[serde(with = "hex::serde")] pub(crate) Vec<u8>);
150
151impl From<Vec<u8>> for Hash {
152 fn from(x: Vec<u8>) -> Self {
153 Self(x)
154 }
155}
156
157impl From<Hash> for Vec<u8> {
158 fn from(x: Hash) -> Self {
159 x.0
160 }
161}
162
163impl AsRef<[u8]> for Hash {
164 fn as_ref(&self) -> &[u8] {
165 &self.0
166 }
167}
168
169impl Deref for Hash {
170 type Target = [u8];
171
172 fn deref(&self) -> &Self::Target {
173 self.as_ref()
174 }
175}
176
177impl Debug for Hash {
178 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
179 <Self as Display>::fmt(self, f)
180 }
181}
182
183impl Display for Hash {
184 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
185 f.write_str(&hex::encode(&self.0))
186 }
187}
188
189#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
193pub enum Algorithm {
194 SHA256,
195}
196
197#[derive(Debug, Clone, Default, Serialize, Deserialize)]
199pub struct Directory {
200 pub files: HashMap<Box<str>, Entry>,
201}
202
203impl Directory {
204 pub(crate) fn search_segments(&self, segments: &[&str]) -> Option<&Entry> {
205 (self.files)
206 .get(segments[0])
207 .and_then(|x| x.search_segments(&segments[1..]))
208 }
209}