mirror of
https://github.com/thegeneralist01/archivr
synced 2026-05-30 08:36:47 +02:00
Handle archive command errors without panics
This commit is contained in:
parent
ce3aaa8b76
commit
4d34ffaa32
1 changed files with 37 additions and 61 deletions
98
src/main.rs
98
src/main.rs
|
|
@ -57,17 +57,17 @@ enum Command {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_archive_path() -> Option<PathBuf> {
|
fn get_archive_path() -> Result<Option<PathBuf>> {
|
||||||
let mut dir = env::current_dir().unwrap();
|
let mut dir = env::current_dir().context("failed to read current working directory")?;
|
||||||
loop {
|
loop {
|
||||||
if dir.join(".archivr").is_dir() {
|
if dir.join(".archivr").is_dir() {
|
||||||
return Some(dir.join(".archivr"));
|
return Ok(Some(dir.join(".archivr")));
|
||||||
}
|
}
|
||||||
if !dir.pop() {
|
if !dir.pop() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
|
@ -91,13 +91,9 @@ use crate::twitter::parse_tweet_id;
|
||||||
|
|
||||||
fn expand_shorthand_to_url(path: &str, source: &Source) -> String {
|
fn expand_shorthand_to_url(path: &str, source: &Source) -> String {
|
||||||
if *source == Source::X && (path.starts_with("tweet:media:") || path.starts_with("x:media:")) {
|
if *source == Source::X && (path.starts_with("tweet:media:") || path.starts_with("x:media:")) {
|
||||||
return format!(
|
if let Some(tweet_id) = path.split(':').next_back().and_then(parse_tweet_id) {
|
||||||
"https://x.com/i/status/{}",
|
return format!("https://x.com/i/status/{tweet_id}");
|
||||||
path.split(':')
|
}
|
||||||
.next_back()
|
|
||||||
.and_then(parse_tweet_id)
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = path.strip_prefix("instagram:") {
|
if let Some(path) = path.strip_prefix("instagram:") {
|
||||||
|
|
@ -294,52 +290,26 @@ fn determine_source(path: &str) -> Source {
|
||||||
Source::Other
|
Source::Other
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_exists(filename: String, store_path: &Path) -> bool {
|
fn hash_exists(hash: &str, file_extension: &str, store_path: &Path) -> Result<bool> {
|
||||||
let mut chars = filename.chars();
|
let path = store_path.join(raw_relative_path_from_hash(hash, file_extension)?);
|
||||||
let first_letter = chars.next().unwrap();
|
|
||||||
let second_letter = chars.next().unwrap();
|
|
||||||
|
|
||||||
let path = store_path
|
|
||||||
.join("raw")
|
|
||||||
.join(first_letter.to_string())
|
|
||||||
.join(second_letter.to_string())
|
|
||||||
.join(filename);
|
|
||||||
|
|
||||||
println!("Checking {}", path.display());
|
println!("Checking {}", path.display());
|
||||||
|
|
||||||
path.exists()
|
Ok(path.exists())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_temp_to_raw(file: &Path, hash: &String, store_path: &Path) -> Result<()> {
|
fn move_temp_to_raw(file: &Path, hash: &str, store_path: &Path) -> Result<()> {
|
||||||
let mut chars = hash.chars();
|
|
||||||
let first_letter = chars.next().unwrap().to_string();
|
|
||||||
let second_letter = chars.next().unwrap().to_string();
|
|
||||||
let file_extension = file
|
let file_extension = file
|
||||||
.extension()
|
.extension()
|
||||||
.map_or(String::new(), |ext| format!(".{}", ext.to_string_lossy()));
|
.map_or(String::new(), |ext| format!(".{}", ext.to_string_lossy()));
|
||||||
|
let raw_relpath = raw_relative_path_from_hash(hash, &file_extension)?;
|
||||||
|
let destination = store_path.join(raw_relpath);
|
||||||
|
|
||||||
fs::create_dir_all(
|
if let Some(parent) = destination.parent() {
|
||||||
store_path
|
fs::create_dir_all(parent)?;
|
||||||
.join("raw")
|
|
||||||
.join(&first_letter)
|
|
||||||
.join(&second_letter),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
fs::rename(
|
|
||||||
file,
|
|
||||||
store_path
|
|
||||||
.join("raw")
|
|
||||||
.join(&first_letter)
|
|
||||||
.join(&second_letter)
|
|
||||||
.join(format!(
|
|
||||||
"{hash}{}",
|
|
||||||
if file_extension.is_empty() {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
&file_extension
|
|
||||||
}
|
}
|
||||||
)),
|
|
||||||
)?;
|
fs::rename(file, destination)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -582,7 +552,7 @@ fn record_tweet_entry(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let tweet_json = fs::read_to_string(store_path.join(&tweet_json_relpath))?;
|
let tweet_json = fs::read_to_string(store_path.join(&tweet_json_relpath))?;
|
||||||
for (role, raw_relpath) in tweet_raw_artifacts(&tweet_json) {
|
for (role, raw_relpath) in tweet_raw_artifacts(&tweet_json)? {
|
||||||
let raw_path = PathBuf::from(&raw_relpath);
|
let raw_path = PathBuf::from(&raw_relpath);
|
||||||
let blob = blob_record_for_raw_relpath(store_path, &raw_path)?;
|
let blob = blob_record_for_raw_relpath(store_path, &raw_path)?;
|
||||||
let blob_id = database::upsert_blob(conn, &blob)?;
|
let blob_id = database::upsert_blob(conn, &blob)?;
|
||||||
|
|
@ -604,8 +574,8 @@ fn record_tweet_entry(
|
||||||
Ok(entry)
|
Ok(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tweet_raw_artifacts(tweet_json: &str) -> Vec<(String, String)> {
|
fn tweet_raw_artifacts(tweet_json: &str) -> Result<Vec<(String, String)>> {
|
||||||
let regex = regex::Regex::new(r#""(avatar_local_path|local_path)": "([^"\n]+)""#).unwrap();
|
let regex = regex::Regex::new(r#""(avatar_local_path|local_path)": "([^"\n]+)""#)?;
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
let mut artifacts = Vec::new();
|
let mut artifacts = Vec::new();
|
||||||
|
|
||||||
|
|
@ -623,7 +593,7 @@ fn tweet_raw_artifacts(tweet_json: &str) -> Vec<(String, String)> {
|
||||||
artifacts.push((role.to_string(), relpath));
|
artifacts.push((role.to_string(), relpath));
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts
|
Ok(artifacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fail_archive_and_exit(
|
fn fail_archive_and_exit(
|
||||||
|
|
@ -643,7 +613,7 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Command::Archive { ref path } => {
|
Command::Archive { ref path } => {
|
||||||
let archive_path = match get_archive_path() {
|
let archive_path = match get_archive_path()? {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => {
|
None => {
|
||||||
eprintln!("Not in an archive. Use 'archivr init' to create one.");
|
eprintln!("Not in an archive. Use 'archivr init' to create one.");
|
||||||
|
|
@ -811,7 +781,7 @@ fn main() -> Result<()> {
|
||||||
.with_context(|| format!("failed to stat staged file {}", temp_file.display()))?
|
.with_context(|| format!("failed to stat staged file {}", temp_file.display()))?
|
||||||
.len() as i64;
|
.len() as i64;
|
||||||
|
|
||||||
let hash_exists = hash_exists(format!("{hash}{file_extension}"), &store_path);
|
let hash_exists = hash_exists(&hash, &file_extension, &store_path)?;
|
||||||
|
|
||||||
// TODO: check for repeated archives?
|
// TODO: check for repeated archives?
|
||||||
// There could be one of the following:
|
// There could be one of the following:
|
||||||
|
|
@ -869,7 +839,9 @@ fn main() -> Result<()> {
|
||||||
} => {
|
} => {
|
||||||
let archive_path = Path::new(&archive_path_string).join(".archivr");
|
let archive_path = Path::new(&archive_path_string).join(".archivr");
|
||||||
let store_path = if Path::new(&store_path_string).is_relative() {
|
let store_path = if Path::new(&store_path_string).is_relative() {
|
||||||
env::current_dir().unwrap().join(store_path_string)
|
env::current_dir()
|
||||||
|
.context("failed to read current working directory")?
|
||||||
|
.join(store_path_string)
|
||||||
} else {
|
} else {
|
||||||
Path::new(store_path_string).to_path_buf()
|
Path::new(store_path_string).to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
@ -899,14 +871,18 @@ fn main() -> Result<()> {
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::create_dir_all(&archive_path).unwrap();
|
fs::create_dir_all(&archive_path)?;
|
||||||
fs::create_dir_all(&store_path).unwrap();
|
fs::create_dir_all(&store_path)?;
|
||||||
fs::write(archive_path.join("name"), archive_name).unwrap();
|
fs::write(archive_path.join("name"), archive_name)?;
|
||||||
let _ = fs::write(
|
fs::write(
|
||||||
archive_path.join("store_path"),
|
archive_path.join("store_path"),
|
||||||
store_path.canonicalize().unwrap().to_str().unwrap(),
|
store_path
|
||||||
);
|
.canonicalize()
|
||||||
initialize_store_directories(&store_path).unwrap();
|
.with_context(|| format!("failed to canonicalize {}", store_path.display()))?
|
||||||
|
.to_str()
|
||||||
|
.context("store path is not valid UTF-8")?,
|
||||||
|
)?;
|
||||||
|
initialize_store_directories(&store_path)?;
|
||||||
let conn = database::open_or_initialize(&archive_path)?;
|
let conn = database::open_or_initialize(&archive_path)?;
|
||||||
let _ = database::ensure_default_user(&conn)?;
|
let _ = database::ensure_default_user(&conn)?;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue