mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-15 03:36:48 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e40d9b1f1 |
No files matched your search
@@ -425,7 +425,10 @@ pub fn open(backend: Box<dyn PresetBackend>) -> Opened {
|
||||
warnings.push(format!("brush presets: {e}"));
|
||||
Vec::new()
|
||||
});
|
||||
let index: Option<IndexFile> = if files.iter().any(|(n, _)| n == INDEX_FILE) {
|
||||
// Optional files are read only when listed: what a read of a missing file (or of a file in a
|
||||
// store folder that doesn't exist yet) reports differs per platform and backend.
|
||||
let listed = |name: &str| files.iter().any(|(n, _)| n == name);
|
||||
let index: Option<IndexFile> = if listed(INDEX_FILE) {
|
||||
match backend.read(INDEX_FILE, MAX_INDEX_BYTES).and_then(|b| serde_json::from_slice(&b).map_err(|e| e.to_string())) {
|
||||
Ok(i) => Some(i),
|
||||
Err(e) => {
|
||||
@@ -489,7 +492,7 @@ pub fn open(backend: Box<dyn PresetBackend>) -> Opened {
|
||||
}
|
||||
}
|
||||
|
||||
let actions = load_actions(backend.as_ref(), &mut warnings);
|
||||
let actions = if listed(ACTIONS_FILE) { load_actions(backend.as_ref(), &mut warnings) } else { Vec::new() };
|
||||
let mut store = PresetStore { backend, synced_rev: None, groups: HashMap::new(), tips: HashMap::new(), index: None, actions_rev: 0, warnings: Vec::new() };
|
||||
let mut presets = Vec::new();
|
||||
for (file, size, g) in parsed {
|
||||
@@ -532,11 +535,10 @@ pub fn open(backend: Box<dyn PresetBackend>) -> Opened {
|
||||
Opened { store, presets, hidden_builtins, order, warnings, actions }
|
||||
}
|
||||
|
||||
/// `actions.json`: a missing file is an empty list. Anything else unreadable is a warning.
|
||||
/// `actions.json`, read when the store lists it. Anything unreadable is a warning.
|
||||
fn load_actions(backend: &dyn PresetBackend, warnings: &mut Vec<String>) -> Vec<crate::actions_cmds::Action> {
|
||||
let bytes = match backend.read(ACTIONS_FILE, MAX_ACTIONS_BYTES) {
|
||||
Ok(b) => b,
|
||||
Err(e) if missing_file(&e) => return Vec::new(),
|
||||
Err(e) => {
|
||||
warnings.push(format!("actions: {ACTIONS_FILE} skipped: {e}"));
|
||||
return Vec::new();
|
||||
@@ -555,11 +557,6 @@ fn load_actions(backend: &dyn PresetBackend, warnings: &mut Vec<String>) -> Vec<
|
||||
}
|
||||
}
|
||||
|
||||
fn missing_file(err: &str) -> bool {
|
||||
let lower = err.to_ascii_lowercase();
|
||||
lower.contains("not found") || lower.contains("no such file") || lower.contains("os error 2")
|
||||
}
|
||||
|
||||
/// Read and decode tips, in parallel on native targets.
|
||||
fn decode_tips(backend: &dyn PresetBackend, hashes: &[String], sizes: &HashMap<String, u64>) -> Vec<Result<GrayTile, String>> {
|
||||
let read = |h: &String| -> Result<Vec<u8>, String> {
|
||||
|
||||
@@ -320,6 +320,41 @@ fn write_failures_are_warnings() {
|
||||
let _ = std::fs::remove_file(&dir.0);
|
||||
}
|
||||
|
||||
/// A store whose folder doesn't exist yet: nothing is listed and every read fails the way Windows
|
||||
/// reports a file in a missing folder (os error 3, not "not found").
|
||||
struct NoFolder;
|
||||
|
||||
impl PresetBackend for NoFolder {
|
||||
fn list(&self) -> Result<Vec<(String, u64)>, String> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
fn read(&self, name: &str, _max: u64) -> Result<Vec<u8>, String> {
|
||||
Err(format!("{name}: The system cannot find the path specified. (os error 3)"))
|
||||
}
|
||||
fn write(&self, _name: &str, _bytes: &[u8]) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
fn remove(&self, _name: &str) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_store_folder_that_does_not_exist_yet_loads_without_warnings() {
|
||||
// A fresh install: the folder appears on the first save, so opening must not warn.
|
||||
let o = open(Box::new(NoFolder));
|
||||
assert!(o.warnings.is_empty() && o.actions.is_empty(), "{:?}", o.warnings);
|
||||
let dir = TempDir::new("fresh");
|
||||
let o = open_dir(dir.0.join("not").join("yet"));
|
||||
assert!(o.warnings.is_empty(), "{:?}", o.warnings);
|
||||
|
||||
// An unreadable actions file that is there is still reported.
|
||||
let mem = MemBackend::default();
|
||||
mem.files.lock().unwrap().insert(ACTIONS_FILE.into(), b"{".to_vec());
|
||||
let o = open(Box::new(mem));
|
||||
assert!(o.warnings.iter().any(|w| w.contains(ACTIONS_FILE)), "{:?}", o.warnings);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backends_refuse_paths_outside_the_store() {
|
||||
let mem = MemBackend::default();
|
||||
|
||||
Reference in new issue
Block a user