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}
33
34pub fn data_id_len(s: &str) -> anyhow::Result<DataIdLen> {
35	match s {
36		_ if s.len() > 65 &&
37			s.bytes().take(64).all(|x| x.is_ascii_hexdigit()) &&
38			s.find(':') == Some(64) &&
39			s[65..].parse::<usize>().is_ok() =>
40		{
41			let mut hash = [0u8; 32];
42			for (i, c) in s.as_bytes()[..64].chunks(2).enumerate() {
43				hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
44			}
45			Ok(DataIdLen::HashLen(hash, s[65..].parse::<usize>().expect("see above")))
46		},
47		_ => Ok(DataIdLen::Data(blob(s)?)),
48	}
49}
50
51#[allow(dead_code)]
52#[derive(Clone, Debug)]
53pub enum DataId {
54	Hash([u8; 32]),
55	Data(Vec<u8>),
56}
57
58#[allow(dead_code)]
59#[allow(clippy::len_without_is_empty)]
60impl DataId {
61	pub fn hash(&self) -> [u8; 32] {
62		match self {
63			DataId::Hash(hash) => *hash,
64			DataId::Data(data) => hash_raw(data),
65		}
66	}
67	pub fn len(&self) -> Option<usize> {
68		match self {
69			DataId::Hash(_) => None,
70			DataId::Data(data) => Some(data.len()),
71		}
72	}
73	pub fn data(&self) -> Option<&[u8]> {
74		match self {
75			DataId::Hash(..) => None,
76			DataId::Data(ref data) => Some(data),
77		}
78	}
79}
80
81#[allow(dead_code)]
82pub fn data_id(s: &str) -> anyhow::Result<DataId> {
83	match s {
84		_ if s.len() == 64 && s.bytes().all(|x| x.is_ascii_hexdigit()) => {
85			let mut hash = [0u8; 32];
86			for (i, c) in s.as_bytes().chunks(2).enumerate() {
87				hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
88			}
89			Ok(DataId::Hash(hash))
90		},
91		_ => Ok(DataId::Data(blob(s)?)),
92	}
93}
94
95pub fn blob(s: &str) -> anyhow::Result<Blob> {
96	match s {
97		_ if (s.starts_with("./") || s.starts_with("/")) && std::path::Path::new(s).exists() =>
98			Ok(std::fs::read(s)?),
99		_ if s.starts_with("0x") &&
100			s.len() % 2 == 0 &&
101			s.bytes().skip(2).all(|x| x.is_ascii_hexdigit()) =>
102		{
103			let mut inner = Vec::with_capacity((s.len() - 2) / 2);
104			for c in s.as_bytes()[2..].chunks(2) {
105				inner.push(u8::from_str_radix(std::str::from_utf8(c)?, 16)?);
106			}
107			Ok(inner)
108		},
109		_ => Ok(s.as_bytes().to_vec()),
110	}
111}
112
113pub fn gas(s: &str) -> anyhow::Result<u64> {
114	if s == "max" {
115		Ok(u64::MAX)
116	} else {
117		Ok(s.parse::<u64>()?)
118	}
119}
120
121pub fn memo(s: &str) -> anyhow::Result<Memo> {
122	match s {
123		_ if s.len() == MEMO_LEN * 2 && s.bytes().all(|x| x.is_ascii_hexdigit()) => {
124			let mut inner = Memo::default();
125			for (i, c) in s.as_bytes().chunks(2).enumerate() {
126				inner[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
127			}
128			Ok(inner)
129		},
130		_ if s.len() <= MEMO_LEN => {
131			let mut inner = Memo::default();
132			inner.0[..s.len()].copy_from_slice(s.as_bytes());
133			Ok(inner)
134		},
135		_ => Err(anyhow::anyhow!("Memo too long")),
136	}
137}
138
139pub fn service_id(s: &str) -> anyhow::Result<ServiceId> {
140	Ok(ServiceId::from_str_radix(s, 16)?)
141}