use std::{
fs::read_dir,
io::Error,
path::PathBuf,
};
use crate::vyper_errors::VyperErrors;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Blueprint {
pub erc_version: u8,
pub preamble_data: Option<Vec<u8>>,
pub initcode: Vec<u8>,
}
pub fn parse_blueprint(bytecode: &[u8]) -> Result<Blueprint, VyperErrors> {
if bytecode.is_empty() {
Err(VyperErrors::BlueprintError("Empty Bytecode".to_owned()))?
}
if &bytecode[0..2] != b"\xFE\x71" {
Err(VyperErrors::BlueprintError("Not a blueprint!".to_owned()))?
}
let erc_version = (&bytecode[2] & 0b11111100) >> 2;
let n_length_bytes = &bytecode[2] & 0b11;
if n_length_bytes == 0b11 {
Err(VyperErrors::BlueprintError("Reserved bits are set".to_owned()))?
}
let size_temp = bytecode[3..(3 + n_length_bytes as usize)].to_vec();
let data_length = match size_temp.len() {
0 => 0,
_ => {
let size: String = hex::encode(&size_temp);
match u32::from_str_radix(&size, size_temp.len() as u32 * 8u32) {
Ok(num) => num,
Err(e) => Err(VyperErrors::IntParseError(e))?,
}
}
};
let preamble_data: Option<Vec<u8>> = match data_length {
0 => None,
_ => {
let data_start = 3 + n_length_bytes as usize;
Some(bytecode[data_start..data_start + data_length as usize].to_vec())
}
};
let initcode =
bytecode[3 + n_length_bytes as usize + data_length as usize..].to_vec();
match initcode.is_empty() {
true => {
Err(VyperErrors::BlueprintError("Empty Initcode!".to_owned()))?
}
false => Ok(Blueprint{erc_version, preamble_data, initcode}),
}
}
pub async fn scan_workspace(root: PathBuf) -> Result<Vec<PathBuf>, Error> {
let cwd = root.clone();
let h1 = tokio::spawn(async move { get_contracts_in_dir(cwd) });
let hh_ape = root.join("contracts");
let h2 = tokio::spawn(async move { get_contracts_in_dir(hh_ape) });
let foundry = root.join("src");
let h3 = tokio::spawn(async move { get_contracts_in_dir(foundry) });
let mut res = Vec::new();
for i in [h1, h2, h3].into_iter() {
let result = match i.await {
Ok(Ok(x)) => x,
_ => Vec::new(),
};
res.push(result)
}
Ok(res.into_iter().flatten().collect::<Vec<PathBuf>>())
}
pub fn get_contracts_in_dir(dir: PathBuf) -> Result<Vec<PathBuf>, Error> {
let files = read_dir(dir)?;
let contracts = files.into_iter().try_fold(
Vec::new(),
|mut acc, x| -> Result<Vec<PathBuf>, Error> {
let file = x?;
if file.path().ends_with(".vy") {
acc.push(file.path())
}
Ok(acc)
},
)?;
Ok(contracts)
}