use std::io::{self, BufRead};
use std::path::Path;
use crate::import::Extractor;
use crate::query::{is_bundle, resolve_backing};
const ENTRY_CAP: usize = 16 << 20;
pub struct Entries<R: BufRead> {
pub reader: R,
pub extractor: Extractor,
pub pending: Option<Vec<u8>>,
pub warned_cap: bool,
}
impl<R: BufRead> Entries<R> {
pub fn next_entry(&mut self) -> io::Result<Option<Vec<u8>>> {
let mut entry = match self.pending.take() {
Some(line) => line,
None => {
let mut line = Vec::new();
if self.reader.read_until(b'\n', &mut line)? == 0 {
return Ok(None);
}
line
}
};
loop {
let mut line = Vec::new();
if self.reader.read_until(b'\n', &mut line)? == 0 {
break;
}
let head = String::from_utf8_lossy(&line[..line.len().min(256)]);
if self.extractor.extract(&head).is_some() {
self.pending = Some(line);
break;
}
if entry.len() + line.len() > ENTRY_CAP {
if !self.warned_cap {
eprintln!("timberfs: entry exceeds 16 MiB; splitting");
self.warned_cap = true;
}
self.pending = Some(line);
break;
}
entry.extend_from_slice(&line);
}
Ok(Some(entry))
}
}
pub fn names_timberfs_source(s: &str) -> bool {
let p = Path::new(s);
if is_bundle(p) {
return p.is_file();
}
match resolve_backing(p) {
Ok((dir, name)) => crate::format::rings_path(&dir, &name).exists(),
Err(_) => false,
}
}
pub fn interior_tokens(lit: &str) -> Vec<String> {
let b = lit.as_bytes();
let mut out: Vec<String> = Vec::new();
let mut i = 0;
while i < b.len() {
if b[i].is_ascii_alphanumeric() {
let start = i;
while i < b.len() && b[i].is_ascii_alphanumeric() {
i += 1;
}
let bounded = start > 0 && i < b.len();
if bounded && (3..=64).contains(&(i - start)) {
out.push(lit[start..i].to_string());
}
} else {
i += 1;
}
}
out.sort();
out.dedup();
out
}
pub fn word_pattern(lit: &str) -> String {
format!(
r"(?:\A|(?-u:[^0-9A-Za-z])){}(?:(?-u:[^0-9A-Za-z])|\z)",
regex::escape(lit)
)
}
pub fn command_line() -> String {
fn quote(a: &str) -> String {
let plain = !a.is_empty()
&& a.bytes()
.all(|b| b.is_ascii_alphanumeric() || b"-_./=:%+,@^".contains(&b));
if plain {
a.to_string()
} else {
format!("'{}'", a.replace('\'', "'\\''"))
}
}
std::iter::once("timberfs".to_string())
.chain(std::env::args().skip(1).map(|a| quote(&a)))
.collect::<Vec<_>>()
.join(" ")
}