use std::path::Path;
use crate::{
Error, Result,
format::{DbFile, DbKind},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileKind {
Regular,
Directory,
Symlink,
}
impl Default for FileKind {
fn default() -> Self {
FileKind::Regular
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileRecord {
pub path: String,
pub packages: Vec<String>,
pub size: u64,
pub kind: FileKind,
pub executable: bool,
pub target: String,
}
#[derive(Debug)]
pub struct PackagesDb {
db: DbFile,
}
impl PackagesDb {
pub(crate) fn from_file(db: DbFile) -> Self {
Self { db }
}
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
let db = DbFile::open(path)?;
if db.kind != DbKind::Packages && db.kind != DbKind::Index {
return Err(Error::InvalidDatabase(
"expected a packages or index database".into(),
));
}
Ok(Self { db })
}
pub fn query(&self, query: &str) -> Result<Vec<FileRecord>> {
let bucket = DbFile::query_bucket(query);
let lines = self.db.bucket_lines(bucket)?;
let mut records = Vec::new();
for line in &lines {
let parts: Vec<&str> = line.splitn(7, '\t').collect();
if parts.is_empty() {
continue;
}
let path = parts[0];
if !path.contains(query) {
continue;
}
let record = if parts.len() >= 6 {
let kind = match parts[1] {
"d" => FileKind::Directory,
"s" => FileKind::Symlink,
_ => FileKind::Regular,
};
let size: u64 = parts[2].parse().unwrap_or(0);
let executable = parts[3] == "1";
let target = parts[4].to_owned();
let packages = parts[5]
.split(',')
.filter(|s| !s.is_empty())
.map(str::to_owned)
.collect();
FileRecord {
path: path.to_owned(),
packages,
size,
kind,
executable,
target,
}
} else if parts.len() >= 2 {
let packages = parts[1]
.split(',')
.filter(|s| !s.is_empty())
.map(str::to_owned)
.collect();
FileRecord {
path: path.to_owned(),
packages,
size: 0,
kind: FileKind::Regular,
executable: false,
target: String::new(),
}
} else {
continue;
};
records.push(record);
}
Ok(records)
}
}