Skip to content

Commit

Permalink
add support for uploading files at the root, panic when fs locks fail
Browse files Browse the repository at this point in the history
  • Loading branch information
jesseditson committed Jul 17, 2024
1 parent 80f8725 commit 8042864
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 51 deletions.
6 changes: 3 additions & 3 deletions src/binary/command/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ pub enum ImportError {
FormatOrFileRequired,
#[error("no file extension or format provided")]
NoExtension,
#[error("invalid object filename {0}")]
#[error("invalid object filename '{0}'")]
InvalidObjectFilename(PathBuf),
#[error("object {0} of type {1} does not exist. found {2:?}")]
ObjectNotExists(String, String, Option<Vec<String>>),
#[error("invalid object path {0}")]
#[error("invalid object path '{0}'")]
InvalidObjectPath(PathBuf),
#[error("invalid object type {0}")]
#[error("invalid object type '{0}'")]
InvalidObjectType(String),
#[error("invalid field {0}")]
InvalidField(String),
Expand Down
41 changes: 27 additions & 14 deletions src/binary/command/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ use thiserror::Error;
pub enum UploadError {
#[error("no access token found. Run archival login first.")]
NotLoggedIn,
#[error("file {0} doesn't exist")]
#[error("file '{0}' doesn't exist")]
FileNotExists(PathBuf),
#[error("current archival manifest does not define archival_site")]
NoArchivalSite,
#[error("invalid object path {0}")]
#[error("invalid object path '{0}'")]
InvalidObjectPath(PathBuf),
#[error("invalid object type {0}")]
#[error("invalid object type '{0}'")]
InvalidObjectType(String),
#[error("invalid field {0}")]
InvalidField(String),
#[error("cannot upload to type {0}")]
#[error("cannot upload to type '{0}'")]
NonUploadableType(String),
#[error("upload failed {0}")]
UploadFailed(String),
Expand Down Expand Up @@ -87,18 +87,23 @@ impl BinaryCommand for Command {
return Err(UploadError::FileNotExists(file_path.to_owned()).into());
}
let object = args.get_one::<PathBuf>("object").unwrap();
// TODO: handle root objects
let object_name = object
.with_extension("")
.file_name()
.ok_or_else(|| UploadError::InvalidObjectPath(object.to_owned()))?
.to_string_lossy()
.to_string();
let object_type = object
let mut object_type = object
.parent()
.ok_or_else(|| UploadError::InvalidObjectPath(object.to_owned()))?
.to_string_lossy()
.to_string();
// If we don't find the object type, it's likely that we mean to use a
// root object, in which case they're the same. A check below will make
// sure that this is truly the case (object_exists)
if object_type.is_empty() {
object_name.clone_into(&mut object_type);
}
let field = args.get_one::<String>("field").unwrap();
let field_path = ValuePath::from_string(field);
// Set up an archival site to make sure we're able to modify fields
Expand All @@ -111,6 +116,10 @@ impl BinaryCommand for Command {
.archival_site
.as_ref()
.ok_or(UploadError::NoArchivalSite)?;
// Make sure that the specified object exists
if !archival.object_exists(&object_type, &object_name)? {
return Err(UploadError::InvalidObjectPath(object.to_owned()).into());
}
// Find the specified object definition
let obj_def = archival
.site
Expand All @@ -125,10 +134,6 @@ impl BinaryCommand for Command {
if !field_def.is_uploadable() {
return Err(UploadError::NonUploadableType(field_def.to_string()).into());
}
// Make sure that the specified object exists
if !archival.object_exists(&object_type, &object_name)? {
return Err(UploadError::InvalidObjectPath(object.to_owned()).into());
}
// Ok, this looks legit. Upload the file
let sha = archival.sha_for_file(file_path)?;
let upload_url = format!("{}/upload/{}/{}", API_URL, archival_site, sha);
Expand Down Expand Up @@ -189,11 +194,19 @@ impl BinaryCommand for Command {
}
// Now write our file
archival.send_event_no_rebuild(ArchivalEvent::EditField(EditFieldEvent {
object: object_type,
filename: object_name,
path: field_path,
value: field_data,
object: object_type.clone(),
filename: object_name.clone(),
path: field_path.clone(),
value: field_data.clone(),
}))?;
if let FieldValue::File(fd) = field_data {
println!(
"Wrote {} {} {}: {:?}",
object_type, object_name, field_path, fd
);
} else {
panic!("Invalid field data");
}
Ok(ExitStatus::Ok)
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/file_system_mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::FileSystemAPI;

#[derive(Error, Debug)]
pub enum FileSystemMutexError {
#[cfg(not(debug_assertions))]
#[error("File System mutex lock failed.")]
LockFailed,
}
Expand All @@ -30,6 +31,9 @@ where
let r = f(fs.deref_mut())?;
Ok(r)
} else {
#[cfg(debug_assertions)]
panic!("File System Lock Failed");
#[cfg(not(debug_assertions))]
Err(FileSystemMutexError::LockFailed.into())
}
}
Expand Down
75 changes: 41 additions & 34 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use std::cmp::Ordering;
use std::collections::HashMap;
use std::error::Error;
use std::path::{Path, PathBuf};
use tracing::{debug, error, warn};
use tracing::{debug, error};
#[cfg(feature = "binary")]
pub mod binary;
mod constants;
Expand Down Expand Up @@ -102,26 +102,25 @@ impl<F: FileSystemAPI> Archival<F> {
}
pub fn object_exists(&self, obj_type: &str, filename: &str) -> Result<bool, Box<dyn Error>> {
self.fs_mutex
.with_fs(|fs| fs.exists(&self.object_path(obj_type, filename)))
.with_fs(|fs| fs.exists(&self.object_path_impl(obj_type, filename, fs)?))
}
pub fn object_path(&self, obj_type: &str, filename: &str) -> PathBuf {
let mut is_root = false;
match self.get_objects() {
Ok(objects) => match &objects.get(obj_type) {
Some(entry) => {
if matches!(entry, ObjectEntry::Object(_)) {
is_root = true;
}
}
None => {
warn!("object type not found: {}", obj_type.to_string());
}
},
Err(e) => {
warn!("Failed getting objects when generating object path: {}", e);
}
}
if is_root {
self.fs_mutex
.with_fs(|fs| Ok(self.object_path_impl(obj_type, filename, fs).unwrap()))
.unwrap()
}
fn object_path_impl(
&self,
obj_type: &str,
filename: &str,
fs: &F,
) -> Result<PathBuf, Box<dyn Error>> {
let objects = self.site.get_objects(fs)?;
let entry = objects.get(obj_type).ok_or(ArchivalError::new(&format!(
"object type not found: {}",
obj_type
)))?;
Ok(if matches!(entry, ObjectEntry::Object(_)) {
self.site
.manifest
.objects_dir
Expand All @@ -132,10 +131,11 @@ impl<F: FileSystemAPI> Archival<F> {
.objects_dir
.join(Path::new(&obj_type))
.join(Path::new(&format!("{}.toml", filename)))
}
})
}
pub fn object_file(&self, obj_type: &str, filename: &str) -> Result<String, Box<dyn Error>> {
self.modify_object_file(obj_type, filename, |o| Ok(o))
self.fs_mutex
.with_fs(|fs| self.modify_object_file(obj_type, filename, |o| Ok(o), fs))
}
pub fn sha_for_file(&self, file: &Path) -> Result<String, Box<dyn Error>> {
let file_data = self
Expand Down Expand Up @@ -167,15 +167,16 @@ impl<F: FileSystemAPI> Archival<F> {
let _ = Object::from_table(obj_def, Path::new(filename), &table)?;
// Object is valid, write it
self.fs_mutex
.with_fs(|fs| fs.write_str(&self.object_path(obj_type, filename), contents))
.with_fs(|fs| fs.write_str(&self.object_path_impl(obj_type, filename, fs)?, contents))
}
fn modify_object_file(
&self,
obj_type: &str,
filename: &str,
obj_cb: impl FnOnce(&mut Object) -> Result<&mut Object, Box<dyn Error>>,
fs: &F,
) -> Result<String, Box<dyn Error>> {
let mut all_objects = self.get_objects()?;
let mut all_objects = self.site.get_objects(fs)?;
if let Some(objects) = all_objects.get_mut(obj_type) {
if let Some(object) = objects.iter_mut().find(|o| o.filename == filename) {
let object = obj_cb(object)?;
Expand Down Expand Up @@ -227,12 +228,13 @@ impl<F: FileSystemAPI> Archival<F> {
"object not found: {}",
event.object
)))?;
let path = self.object_path(&obj_def.name, &event.filename);
self.fs_mutex.with_fs(|fs| {
let path = self.object_path_impl(&obj_def.name, &event.filename, fs)?;
let object = Object::from_def(obj_def, &event.filename, event.order)?;
fs.write_str(&path, object.to_toml()?)
fs.write_str(&path, object.to_toml()?)?;
self.site.invalidate_file(&path);
Ok(())
})?;
self.site.invalidate_file(&path);
Ok(ArchivalEventResponse::None)
}

Expand All @@ -248,9 +250,12 @@ impl<F: FileSystemAPI> Archival<F> {
"object not found: {}",
event.object
)))?;
let path = self.object_path(&obj_def.name, &event.filename);
self.fs_mutex.with_fs(|fs| fs.delete(&path))?;
self.site.invalidate_file(&path);
self.fs_mutex.with_fs(|fs| {
let path = self.object_path_impl(&obj_def.name, &event.filename, fs)?;
fs.delete(&path)?;
self.site.invalidate_file(&path);
Ok(())
})?;
Ok(ArchivalEventResponse::None)
}

Expand Down Expand Up @@ -316,11 +321,13 @@ impl<F: FileSystemAPI> Archival<F> {
obj_cb: impl FnOnce(&mut Object) -> Result<&mut Object, Box<dyn Error>>,
) -> Result<(), Box<dyn Error>> {
debug!("write {}", filename);
let path = self.object_path(obj_type, filename);
let contents = self.modify_object_file(obj_type, filename, obj_cb)?;
self.fs_mutex.with_fs(|fs| fs.write_str(&path, contents))?;
self.site.invalidate_file(&path);
Ok(())
self.fs_mutex.with_fs(|fs| {
let path = self.object_path_impl(obj_type, filename, fs)?;
let contents = self.modify_object_file(obj_type, filename, obj_cb, fs)?;
fs.write_str(&path, contents)?;
self.site.invalidate_file(&path);
Ok(())
})
}

pub fn modify_manifest(
Expand Down

0 comments on commit 8042864

Please sign in to comment.