use jam_std_common::hash_raw;
use jam_types::{Memo, ServiceId, MEMO_LEN};
pub type Blob = Vec<u8>;
#[derive(Clone, Debug)]
#[allow(clippy::len_without_is_empty)]
pub enum DataIdLen {
HashLen([u8; 32], usize),
Data(Vec<u8>),
}
impl DataIdLen {
pub fn hash(&self) -> [u8; 32] {
match self {
DataIdLen::HashLen(hash, _) => *hash,
DataIdLen::Data(data) => hash_raw(data),
}
}
pub fn len(&self) -> usize {
match self {
DataIdLen::HashLen(_, l) => *l,
DataIdLen::Data(data) => data.len(),
}
}
pub fn data(&self) -> Option<&[u8]> {
match self {
DataIdLen::HashLen(..) => None,
DataIdLen::Data(ref data) => Some(data),
}
}
}
pub fn data_id_len(s: &str) -> anyhow::Result<DataIdLen> {
match s {
_ if s.len() > 65 &&
s.bytes().take(64).all(|x| x.is_ascii_hexdigit()) &&
s.find(':') == Some(64) &&
s[65..].parse::<usize>().is_ok() =>
{
let mut hash = [0u8; 32];
for (i, c) in s.as_bytes()[..64].chunks(2).enumerate() {
hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
}
Ok(DataIdLen::HashLen(hash, s[65..].parse::<usize>().expect("see above")))
},
_ => Ok(DataIdLen::Data(blob(s)?)),
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub enum DataId {
Hash([u8; 32]),
Data(Vec<u8>),
}
#[allow(dead_code)]
#[allow(clippy::len_without_is_empty)]
impl DataId {
pub fn hash(&self) -> [u8; 32] {
match self {
DataId::Hash(hash) => *hash,
DataId::Data(data) => hash_raw(data),
}
}
pub fn len(&self) -> Option<usize> {
match self {
DataId::Hash(_) => None,
DataId::Data(data) => Some(data.len()),
}
}
pub fn data(&self) -> Option<&[u8]> {
match self {
DataId::Hash(..) => None,
DataId::Data(ref data) => Some(data),
}
}
}
#[allow(dead_code)]
pub fn data_id(s: &str) -> anyhow::Result<DataId> {
match s {
_ if s.len() == 64 && s.bytes().all(|x| x.is_ascii_hexdigit()) => {
let mut hash = [0u8; 32];
for (i, c) in s.as_bytes().chunks(2).enumerate() {
hash[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
}
Ok(DataId::Hash(hash))
},
_ => Ok(DataId::Data(blob(s)?)),
}
}
pub fn blob(s: &str) -> anyhow::Result<Blob> {
match s {
_ if (s.starts_with("./") || s.starts_with("/")) && std::path::Path::new(s).exists() =>
Ok(std::fs::read(s)?),
_ if s.starts_with("0x") &&
s.len() % 2 == 0 &&
s.bytes().skip(2).all(|x| x.is_ascii_hexdigit()) =>
{
let mut inner = Vec::with_capacity((s.len() - 2) / 2);
for c in s.as_bytes()[2..].chunks(2) {
inner.push(u8::from_str_radix(std::str::from_utf8(c)?, 16)?);
}
Ok(inner)
},
_ => Ok(s.as_bytes().to_vec()),
}
}
pub fn gas(s: &str) -> anyhow::Result<u64> {
if s == "max" {
Ok(u64::MAX)
} else {
Ok(s.parse::<u64>()?)
}
}
pub fn memo(s: &str) -> anyhow::Result<Memo> {
match s {
_ if s.len() == MEMO_LEN * 2 && s.bytes().all(|x| x.is_ascii_hexdigit()) => {
let mut inner = Memo::default();
for (i, c) in s.as_bytes().chunks(2).enumerate() {
inner[i] = u8::from_str_radix(std::str::from_utf8(c)?, 16)?;
}
Ok(inner)
},
_ if s.len() <= MEMO_LEN => {
let mut inner = Memo::default();
inner.0[..s.len()].copy_from_slice(s.as_bytes());
Ok(inner)
},
_ => Err(anyhow::anyhow!("Memo too long")),
}
}
pub fn service_id(s: &str) -> anyhow::Result<ServiceId> {
Ok(ServiceId::from_str_radix(s, 16)?)
}