use std::{fs, ops::Deref, path::Path};
use crate::{Backend, Btf, Error, Result, Type};
pub struct BtfCollection {
base: NamedBtf,
split: Vec<NamedBtf>,
}
pub struct NamedBtf {
pub name: String,
pub btf: Btf,
}
impl Deref for NamedBtf {
type Target = Btf;
fn deref(&self) -> &Self::Target {
&self.btf
}
}
impl BtfCollection {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<BtfCollection> {
Ok(BtfCollection {
base: NamedBtf {
name: Self::file_name(path.as_ref())?,
btf: Btf::from_file(path)?,
},
split: Vec::new(),
})
}
pub fn from_file_with_backend<P: AsRef<Path>>(
path: P,
backend: Backend,
) -> Result<BtfCollection> {
Ok(BtfCollection {
base: NamedBtf {
name: Self::file_name(path.as_ref())?,
btf: Btf::from_file_with_backend(path, backend)?,
},
split: Vec::new(),
})
}
pub fn from_bytes(name: &str, bytes: &[u8]) -> Result<BtfCollection> {
Ok(BtfCollection {
base: NamedBtf {
name: name.to_string(),
btf: Btf::from_bytes(bytes)?,
},
split: Vec::new(),
})
}
pub fn add_split_btf_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<&mut Self> {
let name = Self::file_name(path.as_ref())?;
if self.split.iter().any(|m| m.name == name) {
return Err(Error::Format(format!(
"Split BTF with name {name} already present"
)));
}
self.split.push(NamedBtf {
name,
btf: Btf::from_split_file(path, &self.base.btf)?,
});
Ok(self)
}
pub fn add_split_btf_from_bytes(&mut self, name: &str, bytes: &[u8]) -> Result<&mut Self> {
let name = name.to_string();
if self.split.iter().any(|m| m.name == name) {
return Err(Error::Format(format!(
"Split BTF with name {name} already present"
)));
}
self.split.push(NamedBtf {
name,
btf: Btf::from_split_bytes(bytes, &self.base.btf)?,
});
Ok(self)
}
pub fn from_dir<P: AsRef<Path>>(dir: P, base: &str) -> Result<BtfCollection> {
let mut sys_btf = BtfCollection::from_file(format!("{}/{base}", dir.as_ref().display()))?;
sys_btf.add_split_from_dir(dir, base)?;
Ok(sys_btf)
}
pub fn from_dir_with_backend<P: AsRef<Path>>(
dir: P,
base: &str,
backend: Backend,
) -> Result<BtfCollection> {
let mut sys_btf =
Self::from_file_with_backend(format!("{}/{base}", dir.as_ref().display()), backend)?;
sys_btf.add_split_from_dir(dir, base)?;
Ok(sys_btf)
}
fn add_split_from_dir<P: AsRef<Path>>(&mut self, dir: P, base: &str) -> Result<()> {
for file in fs::read_dir(dir.as_ref())? {
match file {
Ok(file) => {
if file.file_name() == base {
continue;
}
if let Ok(ft) = file.file_type() {
if !ft.is_dir() {
self.add_split_btf_from_file(file.path())?;
}
}
}
Err(e) => return Err(Error::IO(e)),
}
}
Ok(())
}
pub fn get_named_btf(&self, name: &str) -> Option<&NamedBtf> {
self.split.iter().find(|m| m.name == name)
}
pub fn resolve_ids_by_name(&self, name: &str) -> Result<Vec<(&NamedBtf, u32)>> {
let mut ids = self
.base
.btf
.resolve_ids_by_name(name)?
.drain(..)
.map(|i| (&self.base, i))
.collect::<Vec<_>>();
for btf in self.split.iter() {
if let Some(split) = btf.split() {
split
.resolve_ids_by_name(name)?
.drain(..)
.for_each(|i| ids.push((btf, i)));
}
}
Ok(ids)
}
#[cfg(feature = "regex")]
pub fn resolve_ids_by_regex(&self, re: ®ex::Regex) -> Result<Vec<(&NamedBtf, u32)>> {
let mut ids = self
.base
.btf
.resolve_ids_by_regex(re)?
.drain(..)
.map(|i| (&self.base, i))
.collect::<Vec<_>>();
for btf in self.split.iter() {
if let Some(split) = btf.split() {
split
.resolve_ids_by_regex(re)?
.drain(..)
.for_each(|i| ids.push((btf, i)));
}
}
Ok(ids)
}
pub fn resolve_types_by_name(&self, name: &str) -> Result<Vec<(&NamedBtf, Type)>> {
let mut types = self
.base
.btf
.resolve_types_by_name(name)?
.drain(..)
.map(|t| (&self.base, t))
.collect::<Vec<_>>();
for btf in self.split.iter() {
if let Some(split) = btf.split() {
split
.resolve_types_by_name(name)?
.drain(..)
.for_each(|t| types.push((btf, t)));
}
}
Ok(types)
}
#[cfg(feature = "regex")]
pub fn resolve_types_by_regex(&self, re: ®ex::Regex) -> Result<Vec<(&NamedBtf, Type)>> {
let mut types = self
.base
.btf
.resolve_types_by_regex(re)?
.drain(..)
.map(|t| (&self.base, t))
.collect::<Vec<_>>();
for btf in self.split.iter() {
if let Some(split) = btf.split() {
split
.resolve_types_by_regex(re)?
.drain(..)
.for_each(|t| types.push((btf, t)));
}
}
Ok(types)
}
fn file_name(path: &Path) -> Result<String> {
Ok(match path.file_name() {
Some(name) => match name.to_str() {
Some(s) => s.to_string(),
None => return Err(Error::Format(format!("Invalid file name {name:?}"))),
},
None => {
return Err(Error::Format(format!(
"Could not get file name from path {}",
path.display()
)))
}
})
}
}