use std::{
fs::{read_dir, File},
io::Read,
path::{Path, PathBuf},
};
use crate::{Error, FFResult};
enum ErrorOrIterator<E, I> {
Error(Option<E>),
Iter(I),
}
impl<T, E, I> Iterator for ErrorOrIterator<E, I>
where
I: Iterator<Item = Result<T, E>>,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
match self {
ErrorOrIterator::Error(opt) => opt.take().map(Err),
ErrorOrIterator::Iter(i) => i.next(),
}
}
}
fn list_recovery_files_inner(
override_home: impl AsRef<Path>,
) -> FFResult<impl Iterator<Item = Result<PathBuf, std::io::Error>>> {
macro_rules! try_eoi {
($result:expr) => {
match $result {
Ok(ok) => ok,
Err(e) => return Some(ErrorOrIterator::Error(Some(e))),
}
};
}
macro_rules! try_some {
($result:expr) => {
match $result {
Ok(ok) => ok,
Err(e) => return Some(Err(e)),
}
};
}
let firefox_root = override_home.as_ref().join(".mozilla/firefox");
let iter = read_dir(firefox_root)?
.filter_map(|entry_res| {
let entry = try_eoi!(entry_res);
let is_default_dir = try_eoi!(entry.file_type()).is_dir()
&& entry
.file_name()
.to_str()
.map(|s| s.contains("default"))
.unwrap_or_default();
if !is_default_dir {
return None;
}
let backups = entry.path().join("sessionstore-backups");
let iter = read_dir(backups).ok()?.filter_map(|entry_res| {
let entry = try_some!(entry_res);
let is_recovery_file = try_some!(entry.file_type()).is_file()
&& entry
.file_name()
.to_str()
.map(|s| s.starts_with("recovery.js"))
.unwrap_or_default();
is_recovery_file.then(|| Ok(entry.path()))
});
Some(ErrorOrIterator::Iter(iter))
})
.flatten();
Ok(iter)
}
pub fn list_recovery_files() -> FFResult<impl Iterator<Item = Result<PathBuf, std::io::Error>>> {
let home_dir = dirs::home_dir().ok_or_else(|| Error::FFDirNotFound("home"))?;
list_recovery_files_inner(home_dir)
}
pub fn decompress_lz4(p: impl AsRef<Path>) -> FFResult<Vec<u8>> {
let mut f = File::open(p)?;
let mut buf = vec![];
f.read_to_end(&mut buf)?;
Ok(lz4_flex::decompress_size_prepended(&buf[8..])?)
}
#[cfg(test)]
mod test {
use std::{path::PathBuf, str::FromStr};
use super::list_recovery_files_inner;
#[test]
fn list_recovery() {
let files: Vec<_> = list_recovery_files_inner("assets/test").unwrap().collect();
let expected = ["assets/test/.mozilla/firefox/5w5airb6.default-release/sessionstore-backups/recovery.jsonlz4"];
let fullmatch = files.iter().zip(&expected).all(|(listed, expected)| {
listed.as_ref().unwrap() == &PathBuf::from_str(expected).unwrap()
});
eprintln!("expected:{expected:?}");
eprintln!("files:{files:?}");
assert!(fullmatch);
}
}