use std::{fs, path::Path};
use crate::error::{Result, RialoError};
pub trait ProgramDataSource: Send + Sync + std::fmt::Debug {
fn load_program_data(&self) -> Result<Vec<u8>>;
fn description(&self) -> String;
}
#[derive(Debug, Clone)]
pub struct FileProgramDataSource {
path: String,
}
impl FileProgramDataSource {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
Self {
path: path.as_ref().to_string_lossy().to_string(),
}
}
}
impl ProgramDataSource for FileProgramDataSource {
fn load_program_data(&self) -> Result<Vec<u8>> {
let data = fs::read(&self.path).map_err(|e| {
RialoError::InvalidInput(format!(
"Failed to read program file '{}': {}",
self.path, e
))
})?;
if data.is_empty() {
return Err(RialoError::InvalidInput(
"Program file is empty".to_string(),
));
}
if data.len() < 4 {
return Err(RialoError::InvalidInput(
"Program file too small to be valid".to_string(),
));
}
let is_ebpf_or_riscv = &data[0..4] == b"\x7fELF"; let is_pvm = &data[0..4] == b"PVM\0";
if !is_ebpf_or_riscv && !is_pvm {
return Err(RialoError::InvalidInput(
format!(
"Invalid program format: expected eBPF, RISC-V, or PVM program (got magic bytes: {:02x} {:02x} {:02x} {:02x})",
data[0], data[1], data[2], data[3]
),
));
}
Ok(data)
}
fn description(&self) -> String {
format!("file:{}", self.path)
}
}
#[derive(Debug, Clone)]
pub struct MemoryProgramDataSource {
data: Vec<u8>,
description: String,
}
impl MemoryProgramDataSource {
pub fn new(data: Vec<u8>, description: Option<String>) -> Self {
Self {
data,
description: description.unwrap_or_else(|| "memory:unknown".to_string()),
}
}
pub fn new_with_default_description(data: Vec<u8>) -> Self {
Self::new(data, None)
}
}
impl ProgramDataSource for MemoryProgramDataSource {
fn load_program_data(&self) -> Result<Vec<u8>> {
if self.data.is_empty() {
return Err(RialoError::InvalidInput(
"Program data is empty".to_string(),
));
}
if self.data.len() < 4 {
return Err(RialoError::InvalidInput(
"Program data too small to be valid".to_string(),
));
}
let is_ebpf_or_riscv = &self.data[0..4] == b"\x7fELF"; let is_pvm = &self.data[0..4] == b"PVM\0";
if !is_ebpf_or_riscv && !is_pvm {
return Err(RialoError::InvalidInput(
format!(
"Invalid program format: expected eBPF, RISC-V, or PVM program (got magic bytes: {:02x} {:02x} {:02x} {:02x})",
self.data[0], self.data[1], self.data[2], self.data[3]
),
));
}
Ok(self.data.clone())
}
fn description(&self) -> String {
self.description.clone()
}
}
impl<P: AsRef<Path>> From<P> for FileProgramDataSource {
fn from(path: P) -> Self {
Self::new(path)
}
}