use crate::*; use std::io::Read; pub struct Program { pub bytecode: Vec, pub path: Option, } impl Program { fn path(&self) -> String { match &self.path { Some(path) => path.as_os_str().to_string_lossy().to_string(), None => String::from(""), } } } /// Load program from path or standard input. pub fn load_program(path: Option<&PathBuf>) -> Program { if let Some(path) = path { if let Ok(program) = load_program_from_file(path) { let length = program.bytecode.len(); let path = program.path(); info!("Loaded program from {path:?} ({length} bytes)"); return program; } else if let Some(program) = load_program_from_bedrock_path(path) { let length = program.bytecode.len(); let path = program.path(); info!("Loaded program from {path:?} ({length} bytes)"); return program; } else { fatal!("Could not read program from {path:?}"); } } else { info!("Reading program from standard input..."); if let Ok(program) = load_program_from_stdin() { let length = program.bytecode.len(); info!("Loaded program from standard input ({length} bytes)"); return program; } else { fatal!("Could not read program from standard input"); } } } /// Attempt to load program from a directory in the BEDROCK_PATH environment variable. fn load_program_from_bedrock_path(path: &Path) -> Option { // Only if the path can be treated as a file name. if path.is_relative() && path.components().count() == 1 { for base_path in std::env::var("BEDROCK_PATH").ok()?.split(':') { let mut base_path = PathBuf::from(base_path); if !base_path.is_absolute() { continue; } base_path.push(path); info!("Attempting to load program from {base_path:?}"); if let Ok(program) = load_program_from_file(&base_path) { return Some(program); } if path.extension().is_some() { continue; } base_path.set_extension("br"); info!("Attempting to load program from {base_path:?}"); if let Ok(program) = load_program_from_file(&base_path) { return Some(program); } } } return None; } /// Attempt to load program from a specific file path. fn load_program_from_file(path: &Path) -> Result { // Canonicalize paths so that symbolic links to program files resolve to // the real program directory, which could contain a symbols file. let path = match path.canonicalize() { Ok(canonical) => canonical, Err(_) => path.to_path_buf(), }; load_program_from_readable_source(std::fs::File::open(&path)?, Some(&path)) } /// Attempt to load program from standard input. fn load_program_from_stdin() -> Result { load_program_from_readable_source(std::io::stdin(), None) } /// Attempt to load program from a source that implements std::io::Read. fn load_program_from_readable_source(source: impl Read, path: Option<&Path>) -> Result { let mut bytecode = Vec::::new(); source.take(65536).read_to_end(&mut bytecode)?; return Ok(Program { bytecode, path: path.map(|p| p.to_path_buf()) }); }