jam_tooling/
parsing.rs

1use jam_std_common::hash_raw;
2use jam_types::{Memo, ServiceId, MEMO_LEN};
3
4pub type Blob = Vec<u8>;
5
6#[derive(Clone, Debug)]
7#[allow(clippy::len_without_is_empty)]
8pub enum DataIdLen {
9	HashLen([u8; 32], usize),
10	Data(Vec<u8>),
11}
12
13impl DataIdLen {
14	pub fn hash(&self) -> [u8; 32] {
15		match self {
16			DataIdLen::HashLen(hash, _) => *hash,
17			DataIdLen::Data(data) => hash_raw(data),
18		}
19	}
20	pub fn len(&self) -> usize {
21		match self {
22			DataIdLen::HashLen(_, l) => *l,
23			DataIdLen::Data(data) => data.len(),
24		}
25	}
26	pub fn data(&self) -> Option<&[u8]> {
27		match self {
28			DataIdLen::HashLen(..) => None,
29			DataIdLen::Data(ref data) => Some(data),
30		}
31	}
32	pub fn into_data(self) -> Option<Vec<u8>> {
33		match self {
34			DataIdLen::HashLen(..) => None,
35			DataIdLen::Data(d) => Some(d),
36		}
37	}
38}
39
40pub fn data_id_len(s: &str) -> anyhow::Result<DataIdLen> {
41	match s {
42		_ if s.starts_with("0x") &&
43			s.len() > 67 &&
44			s.bytes().skip(2).take(64).all(|x| x.is_ascii_hexdigit()) &&
45			s.find(':') == Some(66) &&
46			s[67..].parse::<usize>().is_ok() =>
47		{
48			let mut hash = [0u8; 32];
49			for (i, c) in s.as_bytes()[2..66].chunks(2).enumerate() {
50				hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
51			}
52			Ok(DataIdLen::HashLen(hash, s[67..].parse::<usize>().expect("see above")))
53		},
54		_ if s.len() > 65 &&
55			s.bytes().take(64).all(|x| x.is_ascii_hexdigit()) &&
56			s.find(':') == Some(64) &&
57			s[65..].parse::<usize>().is_ok() =>
58		{
59			let mut hash = [0u8; 32];
60			for (i, c) in s.as_bytes()[..64].chunks(2).enumerate() {
61				hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
62			}
63			Ok(DataIdLen::HashLen(hash, s[65..].parse::<usize>().expect("see above")))
64		},
65		_ => Ok(DataIdLen::Data(blob(s)?)),
66	}
67}
68
69#[allow(dead_code)]
70#[derive(Clone, Debug)]
71pub enum DataId {
72	Hash([u8; 32]),
73	Data(Vec<u8>),
74}
75
76#[allow(dead_code)]
77#[allow(clippy::len_without_is_empty)]
78impl DataId {
79	pub fn hash(&self) -> [u8; 32] {
80		match self {
81			DataId::Hash(hash) => *hash,
82			DataId::Data(data) => hash_raw(data),
83		}
84	}
85	pub fn len(&self) -> Option<usize> {
86		match self {
87			DataId::Hash(_) => None,
88			DataId::Data(data) => Some(data.len()),
89		}
90	}
91	pub fn data(&self) -> Option<&[u8]> {
92		match self {
93			DataId::Hash(..) => None,
94			DataId::Data(ref data) => Some(data),
95		}
96	}
97}
98
99pub fn data_id(s: &str) -> anyhow::Result<DataId> {
100	match s {
101		_ if s.len() == 64 && s.bytes().all(|x| x.is_ascii_hexdigit()) => {
102			let mut hash = [0u8; 32];
103			for (i, c) in s.as_bytes().chunks(2).enumerate() {
104				hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
105			}
106			Ok(DataId::Hash(hash))
107		},
108		_ if s.starts_with("0x") &&
109			s.len() == 66 &&
110			s.bytes().skip(2).all(|x| x.is_ascii_hexdigit()) =>
111		{
112			let mut hash = [0u8; 32];
113			for (i, c) in s.as_bytes().chunks(2).skip(1).enumerate() {
114				hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
115			}
116			Ok(DataId::Hash(hash))
117		},
118		_ => Ok(DataId::Data(blob(s)?)),
119	}
120}
121
122pub fn blob(s: &str) -> anyhow::Result<Blob> {
123	match s {
124		_ if std::path::Path::new(s).exists() => Ok(std::fs::read(s)?),
125		_ if s.starts_with("0x") &&
126			s.len() % 2 == 0 &&
127			s.bytes().skip(2).all(|x| x.is_ascii_hexdigit()) =>
128		{
129			let mut inner = Vec::with_capacity((s.len() - 2) / 2);
130			for c in s.as_bytes()[2..].chunks(2) {
131				inner.push(u8::from_str_radix(std::str::from_utf8(c)?, 16)?);
132			}
133			Ok(inner)
134		},
135		_ => Ok(s.as_bytes().to_vec()),
136	}
137}
138
139pub fn gas(s: &str) -> anyhow::Result<u64> {
140	if s == "max" {
141		Ok(u64::MAX)
142	} else {
143		Ok(s.parse::<u64>()?)
144	}
145}
146
147pub fn memo(s: &str) -> anyhow::Result<Memo> {
148	match s {
149		_ if s.len() == MEMO_LEN * 2 && s.bytes().all(|x| x.is_ascii_hexdigit()) => {
150			let mut inner = Memo::default();
151			for (i, c) in s.as_bytes().chunks(2).enumerate() {
152				inner[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
153			}
154			Ok(inner)
155		},
156		_ if s.len() <= MEMO_LEN => {
157			let mut inner = Memo::default();
158			inner.0[..s.len()].copy_from_slice(s.as_bytes());
159			Ok(inner)
160		},
161		_ => Err(anyhow::anyhow!("Memo too long")),
162	}
163}
164
165pub fn service_id(s: &str) -> anyhow::Result<ServiceId> {
166	Ok(ServiceId::from_str_radix(s, 16)?)
167}