Merge pull request #157 from storytold/fix/issue-37
Fix #37: a name typed without an extension gets the chosen format's on Export too
This commit is contained in:
commit
cccdf96cc1
|
|
@ -134,12 +134,25 @@ pub(crate) fn write_named(app: &mut VectorcraftApp, path: Option<String>, name:
|
|||
}
|
||||
|
||||
/// Where a file goes when no path was given: the suggested name on the web (a download), else the
|
||||
/// save panel's choice.
|
||||
/// save panel's choice, given the suggested name's extension when it names no file type (a name
|
||||
/// typed without one).
|
||||
fn pick_path(app: &mut VectorcraftApp, pick: &FilePick) -> Result<String, String> {
|
||||
if is_web(app) {
|
||||
return Ok(pick.name.clone());
|
||||
}
|
||||
app.services.pick_save.as_mut().and_then(|f| f(pick)).ok_or_else(|| "cancelled".into())
|
||||
let picked = app.services.pick_save.as_mut().and_then(|f| f(pick)).ok_or("cancelled")?;
|
||||
Ok(with_extension(&picked, &fileio::extension(&pick.name), |_| true))
|
||||
}
|
||||
|
||||
/// `path` as typed in a save panel: kept when it ends in `ext` or its extension names a format
|
||||
/// `keeps` takes, else with `.ext` added.
|
||||
fn with_extension(path: &str, ext: &str, keeps: impl Fn(&Format) -> bool) -> String {
|
||||
let has = fileio::extension(path);
|
||||
if ext.is_empty() || has.eq_ignore_ascii_case(ext) || fileio::format_for_name(path).is_some_and(keeps) {
|
||||
path.to_string()
|
||||
} else {
|
||||
format!("{path}.{ext}")
|
||||
}
|
||||
}
|
||||
|
||||
/// The active document's file name with extension `ext`, and its folder.
|
||||
|
|
@ -172,10 +185,7 @@ pub fn remember_view(app: &mut VectorcraftApp) {
|
|||
/// A path typed in a save panel: kept when its extension names a save format (the panel's file
|
||||
/// type), else `f`'s extension is added.
|
||||
fn with_save_extension(path: &str, f: &Format) -> String {
|
||||
match fileio::format_for_name(path) {
|
||||
Some(g) if SAVE_FORMATS.contains(&g.id) => path.to_string(),
|
||||
_ => format!("{path}.{}", f.extensions[0]),
|
||||
}
|
||||
with_extension(path, f.extensions[0], |g| SAVE_FORMATS.contains(&g.id))
|
||||
}
|
||||
|
||||
/// Write every file of an export (one per artboard, linked images) for the destination `path`.
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ mod tests_recovery;
|
|||
#[cfg(test)]
|
||||
mod tests_save;
|
||||
#[cfg(test)]
|
||||
mod tests_saveext;
|
||||
#[cfg(test)]
|
||||
mod tests_slices;
|
||||
#[cfg(test)]
|
||||
mod tests_svg;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
//! A name typed in a save panel without an extension gets the chosen format's: Save, Save As,
|
||||
//! Save a Copy, Export, Export Selection and other writes through a save panel.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use serde_json::json;
|
||||
use vectorcraft_engine::Session;
|
||||
|
||||
use crate::{FilePick, Services, VectorcraftApp, io};
|
||||
|
||||
type Written = Rc<RefCell<Vec<String>>>;
|
||||
|
||||
/// An app with a document whose save panel answers `typed` and whose writer records the paths.
|
||||
fn saving_app(typed: &'static str) -> (VectorcraftApp, Written) {
|
||||
let written = Written::default();
|
||||
let w = written.clone();
|
||||
let services = Services {
|
||||
write: Some(Box::new(move |p: &str, _: &[u8]| {
|
||||
w.borrow_mut().push(p.to_string());
|
||||
Ok(())
|
||||
})),
|
||||
pick_save: Some(Box::new(move |_: &FilePick| Some(typed.to_string()))),
|
||||
..Default::default()
|
||||
};
|
||||
let mut app = VectorcraftApp::new(Session::new(), services);
|
||||
app.run("file.new", json!({"width": 100, "height": 100})).unwrap();
|
||||
app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 30, "height": 20})).unwrap();
|
||||
(app, written)
|
||||
}
|
||||
|
||||
fn last(w: &Written) -> String {
|
||||
w.borrow().last().cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saves_add_the_format_extension_to_a_bare_name() {
|
||||
let (mut app, w) = saving_app("/tmp/Untitled-1");
|
||||
app.run("file.save", json!({})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/Untitled-1.vectorcraft");
|
||||
assert_eq!(app.session.active().unwrap().path.as_deref(), Some("/tmp/Untitled-1.vectorcraft"));
|
||||
let (mut app, w) = saving_app("/tmp/copy");
|
||||
app.run("file.saveCopy", json!({"format": "svg", "svg": {}})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/copy.svg");
|
||||
let (mut app, w) = saving_app("/tmp/as");
|
||||
app.run("file.saveAs", json!({"format": "pdf", "options": {}})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/as.pdf");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exports_add_the_format_extension_to_a_bare_name() {
|
||||
let (mut app, w) = saving_app("/tmp/art");
|
||||
io::export(&mut app, Some("png"), None, &json!({})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/art.png");
|
||||
app.run("select.all", json!({})).unwrap();
|
||||
app.run("document.exportSelection", json!({})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/art.png", "Export Selection: its default format");
|
||||
io::save_command_output(&mut app, "document.export", "txt", json!({"format": "txt"})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/art.txt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_extensions_are_kept() {
|
||||
let (mut app, w) = saving_app("/tmp/art.svg");
|
||||
io::export(&mut app, Some("svg"), None, &json!({})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/art.svg");
|
||||
let (mut app, w) = saving_app("/tmp/My.Poster.SVG");
|
||||
io::export(&mut app, Some("svg"), None, &json!({})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/My.Poster.SVG");
|
||||
// A dot in the name isn't an extension.
|
||||
let (mut app, w) = saving_app("/tmp/v1.2");
|
||||
io::export(&mut app, Some("png"), None, &json!({})).unwrap();
|
||||
assert_eq!(last(&w), "/tmp/v1.2.png");
|
||||
}
|
||||
Loading…
Reference in New Issue