mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-19 09:22:12 +00:00
cleaning
This commit is contained in:
1 parent
8fd47d8b18
commit
5373d0f0d4
15 files changed
+339
-408
No files matched your search
+60
-105
@@ -6,9 +6,9 @@
|
||||
//! open if it does not say.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use gpx_engine::{ExportOptions, api};
|
||||
use gpx_engine::{ExportOptions, FileId, api};
|
||||
use serde::Deserialize;
|
||||
use tauri::{AppHandle, Runtime, State};
|
||||
use tauri_plugin_dialog::{DialogExt, FilePath};
|
||||
@@ -25,6 +25,32 @@ fn paths(picked: Vec<FilePath>) -> Vec<PathBuf> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Asks where to write a GPX file, proposing `file_name` in `folder`. `None` if the user cancelled.
|
||||
fn ask_save_path<R: Runtime>(
|
||||
app: &AppHandle<R>,
|
||||
file_name: &str,
|
||||
folder: Option<&Path>,
|
||||
) -> Option<PathBuf> {
|
||||
let mut dialog = app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter("GPX", &["gpx"])
|
||||
.set_file_name(file_name);
|
||||
if let Some(folder) = folder {
|
||||
dialog = dialog.set_directory(folder);
|
||||
}
|
||||
dialog.blocking_save_file()?.into_path().ok()
|
||||
}
|
||||
|
||||
/// Asks for a folder, starting in `folder`. `None` if the user cancelled.
|
||||
fn ask_folder<R: Runtime>(app: &AppHandle<R>, folder: Option<&Path>) -> Option<PathBuf> {
|
||||
let mut dialog = app.dialog().file();
|
||||
if let Some(folder) = folder {
|
||||
dialog = dialog.set_directory(folder);
|
||||
}
|
||||
dialog.blocking_pick_folder()?.into_path().ok()
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn workspace_state(state: State<AppState>) -> WorkspaceState {
|
||||
state.core(|core| core.workspace_state())
|
||||
@@ -46,7 +72,7 @@ pub fn open_files<R: Runtime>(app: AppHandle<R>, state: State<AppState>) -> api:
|
||||
|
||||
/// Opens files that are on the disk, like the ones of the tree of the open folder. They get selected.
|
||||
/// With `preview`, a single file is open as a preview (a single click in the tree, see
|
||||
/// `Workspace::open_preview`); otherwise they are open for good, even the preview.
|
||||
/// `Workspace::open`); otherwise they are open for good, even the preview.
|
||||
#[tauri::command(async)]
|
||||
pub fn open_paths(
|
||||
state: State<AppState>,
|
||||
@@ -54,18 +80,10 @@ pub fn open_paths(
|
||||
preview: Option<bool>,
|
||||
) -> api::Outcome {
|
||||
state.core(move |core| {
|
||||
let outcome = match paths.as_slice() {
|
||||
[path] if preview == Some(true) => core.workspace.open_preview(&mut core.engine, path),
|
||||
_ => {
|
||||
let outcome = core.workspace.open_files(&mut core.engine, &paths, true);
|
||||
let ids = core.workspace.ids_at(&paths);
|
||||
core.workspace.keep(&ids);
|
||||
outcome
|
||||
}
|
||||
};
|
||||
core.keep_workspace();
|
||||
core.persist();
|
||||
core.announce(None);
|
||||
let outcome = core
|
||||
.workspace
|
||||
.open(&mut core.engine, &paths, preview.unwrap_or_default());
|
||||
core.commit(None);
|
||||
outcome
|
||||
})
|
||||
}
|
||||
@@ -93,38 +111,16 @@ fn write_unsaved<R: Runtime>(
|
||||
let mut folder = None;
|
||||
match unlinked.as_slice() {
|
||||
[] => {}
|
||||
[(id, name)] => {
|
||||
let mut dialog = app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter("GPX", &["gpx"])
|
||||
.set_file_name(format!("{name}.gpx"));
|
||||
if let Some(start) = &start {
|
||||
dialog = dialog.set_directory(start);
|
||||
[(id, name)] => match ask_save_path(app, &format!("{name}.gpx"), start.as_deref()) {
|
||||
Some(path) => {
|
||||
chosen.insert(*id, path);
|
||||
}
|
||||
match dialog
|
||||
.blocking_save_file()
|
||||
.and_then(|path| path.into_path().ok())
|
||||
{
|
||||
Some(path) => {
|
||||
chosen.insert(*id, path);
|
||||
}
|
||||
None => return Ok(false),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mut dialog = app.dialog().file();
|
||||
if let Some(start) = &start {
|
||||
dialog = dialog.set_directory(start);
|
||||
}
|
||||
match dialog
|
||||
.blocking_pick_folder()
|
||||
.and_then(|path| path.into_path().ok())
|
||||
{
|
||||
Some(path) => folder = Some(path),
|
||||
None => return Ok(false),
|
||||
}
|
||||
}
|
||||
None => return Ok(false),
|
||||
},
|
||||
_ => match ask_folder(app, start.as_deref()) {
|
||||
Some(path) => folder = Some(path),
|
||||
None => return Ok(false),
|
||||
},
|
||||
}
|
||||
state.core(move |core| {
|
||||
let result = core
|
||||
@@ -157,7 +153,7 @@ pub fn save_file_as<R: Runtime>(
|
||||
let (name, current, folder) = state.core(move |core| {
|
||||
let name = core
|
||||
.engine
|
||||
.file_structure(&gpx_engine::FileId(file_id))
|
||||
.file_structure(&FileId(file_id))
|
||||
.map(|structure| structure.name.clone());
|
||||
let status = core.workspace_state();
|
||||
let file = status.files.get(&file_id.to_string());
|
||||
@@ -170,27 +166,12 @@ pub fn save_file_as<R: Runtime>(
|
||||
let Some(name) = name else {
|
||||
return Ok(false);
|
||||
};
|
||||
let mut dialog = app.dialog().file().add_filter("GPX", &["gpx"]);
|
||||
match ¤t {
|
||||
Some(path) => {
|
||||
if let Some(file_name) = path.file_name() {
|
||||
dialog = dialog.set_file_name(file_name.to_string_lossy());
|
||||
}
|
||||
if let Some(parent) = path.parent() {
|
||||
dialog = dialog.set_directory(parent);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
dialog = dialog.set_file_name(format!("{name}.gpx"));
|
||||
if let Some(folder) = &folder {
|
||||
dialog = dialog.set_directory(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(path) = dialog
|
||||
.blocking_save_file()
|
||||
.and_then(|path| path.into_path().ok())
|
||||
else {
|
||||
// where the file is, or else where it would be written
|
||||
let folder = current
|
||||
.as_ref()
|
||||
.and_then(|path| path.parent().map(Path::to_path_buf))
|
||||
.or(folder);
|
||||
let Some(path) = ask_save_path(&app, &format!("{name}.gpx"), folder.as_deref()) else {
|
||||
return Ok(false);
|
||||
};
|
||||
state.core(move |core| {
|
||||
@@ -204,9 +185,7 @@ pub fn save_file_as<R: Runtime>(
|
||||
pub fn reload_files(state: State<AppState>, file_ids: Vec<Uuid>) -> Result<api::Outcome, String> {
|
||||
state.core(move |core| {
|
||||
let outcome = core.workspace.reload(&mut core.engine, &file_ids);
|
||||
core.keep_workspace();
|
||||
core.persist();
|
||||
core.announce(None);
|
||||
core.commit(None);
|
||||
outcome
|
||||
})
|
||||
}
|
||||
@@ -255,10 +234,7 @@ pub fn open_folder<R: Runtime>(
|
||||
let outcome = core
|
||||
.workspace
|
||||
.open_folder(&mut core.engine, &folder, discard);
|
||||
core.keep_workspace();
|
||||
core.persist();
|
||||
core.watch();
|
||||
core.announce(None);
|
||||
core.commit(None);
|
||||
Ok(outcome)
|
||||
})
|
||||
}
|
||||
@@ -279,10 +255,7 @@ pub fn close_folder<R: Runtime>(
|
||||
let outcome = core
|
||||
.workspace
|
||||
.close_folder(&mut core.engine, unsaved.is_some());
|
||||
core.keep_workspace();
|
||||
core.persist();
|
||||
core.watch();
|
||||
core.announce(None);
|
||||
core.commit(None);
|
||||
Ok(outcome)
|
||||
})
|
||||
}
|
||||
@@ -303,9 +276,7 @@ pub fn close_files<R: Runtime>(
|
||||
let outcome = core
|
||||
.workspace
|
||||
.close_files(&mut core.engine, &file_ids, unsaved.is_some());
|
||||
core.keep_workspace();
|
||||
core.persist();
|
||||
core.announce(None);
|
||||
core.commit(None);
|
||||
Ok(outcome)
|
||||
})
|
||||
}
|
||||
@@ -364,7 +335,7 @@ pub fn export_files<R: Runtime>(
|
||||
file_ids
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
let name = core.workspace.display_name(&core.engine, *id)?;
|
||||
let name = core.engine.file_structure(&FileId(*id))?.name.clone();
|
||||
Some((name, api::export_file(&core.engine, *id, options)?))
|
||||
})
|
||||
.collect()
|
||||
@@ -376,25 +347,13 @@ pub fn export_files<R: Runtime>(
|
||||
match files.as_slice() {
|
||||
[] => Ok(false),
|
||||
[(name, bytes)] => {
|
||||
let Some(path) = app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter("GPX", &["gpx"])
|
||||
.set_file_name(format!("{name}.gpx"))
|
||||
.blocking_save_file()
|
||||
.and_then(|path| path.into_path().ok())
|
||||
else {
|
||||
let Some(path) = ask_save_path(&app, &format!("{name}.gpx"), None) else {
|
||||
return Ok(false);
|
||||
};
|
||||
write(path, bytes).map(|()| true)
|
||||
}
|
||||
_ => {
|
||||
let Some(folder) = app
|
||||
.dialog()
|
||||
.file()
|
||||
.blocking_pick_folder()
|
||||
.and_then(|path| path.into_path().ok())
|
||||
else {
|
||||
let Some(folder) = ask_folder(&app, None) else {
|
||||
return Ok(false);
|
||||
};
|
||||
let mut written: Vec<PathBuf> = vec![];
|
||||
@@ -433,14 +392,13 @@ pub fn rename_path(
|
||||
let selected = api::select(&mut core.engine, &[id], gpx_engine::SelectMode::Replace);
|
||||
let desc = core
|
||||
.engine
|
||||
.file_structure(&gpx_engine::FileId(id))
|
||||
.file_structure(&FileId(id))
|
||||
.and_then(|structure| structure.desc.clone())
|
||||
.unwrap_or_default();
|
||||
return Ok(selected.merge(edit::rename_selection(core, name, desc)));
|
||||
}
|
||||
core.workspace.rename_path(&path, &name)?;
|
||||
core.keep_workspace();
|
||||
core.announce(None);
|
||||
core.commit(None);
|
||||
Ok(api::Outcome::failed_none())
|
||||
})
|
||||
}
|
||||
@@ -454,8 +412,7 @@ pub fn move_paths(
|
||||
) -> Result<(), String> {
|
||||
state.core(move |core| {
|
||||
let result = core.workspace.move_paths(&paths, &folder);
|
||||
core.keep_workspace();
|
||||
core.announce(None);
|
||||
core.commit(None);
|
||||
result
|
||||
})
|
||||
}
|
||||
@@ -468,9 +425,7 @@ pub fn delete_paths(state: State<AppState>, paths: Vec<PathBuf>) -> Result<api::
|
||||
let outcome = core
|
||||
.workspace
|
||||
.delete_paths(&mut core.engine, &paths, to_trash)?;
|
||||
core.keep_workspace();
|
||||
core.persist();
|
||||
core.announce(None);
|
||||
core.commit(None);
|
||||
Ok(outcome)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ pub fn rename_selection(core: &mut Core, name: String, desc: String) -> api::Out
|
||||
let mut name = name;
|
||||
if let Selection::File { file_ids } = core.engine.selection() {
|
||||
for FileId(id) in file_ids.clone() {
|
||||
name = core.workspace.valid_name(id, &name);
|
||||
if let Err(error) = core.workspace.renamed_path(id, &name) {
|
||||
return api::Outcome::failed(error);
|
||||
match core.workspace.name_for(id, &name) {
|
||||
Ok(valid) => name = valid,
|
||||
Err(error) => return api::Outcome::failed(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-49
@@ -18,7 +18,7 @@ use notify_debouncer_mini::{DebounceEventResult, Debouncer, new_debouncer};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::store::FileStorage;
|
||||
use crate::workspace::{Workspace, WorkspaceState};
|
||||
use crate::workspace::{Workspace, WorkspaceState, signature};
|
||||
|
||||
/// Tells the frontend that something happened that it did not ask for: a name and a JSON value.
|
||||
pub type Emit = Arc<dyn Fn(&str, serde_json::Value) + Send + Sync>;
|
||||
@@ -89,13 +89,9 @@ impl Core {
|
||||
if paths.is_empty() {
|
||||
return;
|
||||
}
|
||||
let outcome = self.workspace.open_files(&mut self.engine, paths, true);
|
||||
let ids = self.workspace.ids_at(paths);
|
||||
self.workspace.keep(&ids);
|
||||
self.keep_workspace();
|
||||
self.persist();
|
||||
let outcome = self.workspace.open(&mut self.engine, paths, false);
|
||||
(self.emit)("files-opened", to_json(&outcome));
|
||||
self.announce(None);
|
||||
self.commit(None);
|
||||
}
|
||||
|
||||
/// Runs `f` on the engine, which can change: what changed is kept, and the files on the disk are
|
||||
@@ -104,22 +100,19 @@ impl Core {
|
||||
fn editing<T>(&mut self, f: impl FnOnce(&mut Engine) -> T) -> (T, Vec<String>) {
|
||||
let names = self.workspace.names(&self.engine);
|
||||
let preview = self.workspace.preview;
|
||||
let preview_before = preview.and_then(|id| crate::workspace::signature(&self.engine, id));
|
||||
let preview_before = preview.and_then(|id| signature(&self.engine, id));
|
||||
let result = f(&mut self.engine);
|
||||
// the preview is open for good once something is done to it
|
||||
if let Some(id) = preview
|
||||
&& crate::workspace::signature(&self.engine, id) != preview_before
|
||||
{
|
||||
let kept = preview.is_some_and(|id| signature(&self.engine, id) != preview_before);
|
||||
if let Some(id) = preview.filter(|_| kept) {
|
||||
self.workspace.keep(&[id]);
|
||||
self.keep_workspace();
|
||||
self.announce(None);
|
||||
}
|
||||
let (renamed, errors) = self.workspace.follow_names(&mut self.engine, &names);
|
||||
if renamed {
|
||||
self.keep_workspace();
|
||||
self.announce(None);
|
||||
if kept || renamed {
|
||||
self.commit(None);
|
||||
} else {
|
||||
self.persist();
|
||||
}
|
||||
self.persist();
|
||||
(result, errors)
|
||||
}
|
||||
|
||||
@@ -139,17 +132,18 @@ impl Core {
|
||||
/// the files they were written to) is kept and told, with where the files are. Says whether
|
||||
/// they were written.
|
||||
pub fn wrote(&mut self, result: Result<api::Outcome, String>) -> Result<bool, String> {
|
||||
self.keep_workspace();
|
||||
match &result {
|
||||
Ok(outcome) if outcome.changed => {
|
||||
self.persist();
|
||||
self.announce(Some(outcome));
|
||||
}
|
||||
_ => self.announce(None),
|
||||
}
|
||||
self.commit(result.as_ref().ok().filter(|outcome| outcome.changed));
|
||||
result.map(|_| true)
|
||||
}
|
||||
|
||||
/// After a change of the files or of where they are: the workspace and the engine are kept, and
|
||||
/// the frontend is told (`outcome` is what changed in the engine, if it did not ask for it).
|
||||
pub fn commit(&mut self, outcome: Option<&api::Outcome>) {
|
||||
self.keep_workspace();
|
||||
self.persist();
|
||||
self.announce(outcome);
|
||||
}
|
||||
|
||||
/// Writes the workspace, and watches what it holds (a file may have been opened or closed).
|
||||
pub fn keep_workspace(&mut self) {
|
||||
self.workspace.write(self.storage.root());
|
||||
@@ -224,9 +218,7 @@ impl Core {
|
||||
(self.emit)("folder-changed", to_json(&in_folder));
|
||||
}
|
||||
if let Some(outcome) = self.workspace.reconcile(&mut self.engine, Some(paths)) {
|
||||
self.keep_workspace();
|
||||
self.persist();
|
||||
self.announce(Some(&outcome));
|
||||
self.commit(Some(&outcome));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,32 +247,15 @@ impl Core {
|
||||
.then(|| self.workspace.folder.clone())
|
||||
.flatten()
|
||||
});
|
||||
// what the files it comes from are called, by the name in them (the copies have the same one)
|
||||
let sources: Vec<(String, String)> = selected
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
let structure = self.engine.file_structure(&gpx_engine::FileId(*id))?;
|
||||
Some((
|
||||
structure.name.clone(),
|
||||
self.workspace.display_name(&self.engine, *id)?,
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
let mut outcome = f(&mut self.engine);
|
||||
if let Some(dir) = dir {
|
||||
self.workspace.inherit(outcome.added(), &dir);
|
||||
// what is made in a folder is written there at once, so that the tree shows it. A copy
|
||||
// is named like the file it is a copy of
|
||||
let made: Vec<(uuid::Uuid, Option<String>)> = outcome
|
||||
// what is made in a folder is written there at once, so that the tree shows it
|
||||
let made: Vec<uuid::Uuid> = outcome
|
||||
.added()
|
||||
.iter()
|
||||
.filter_map(|id| uuid::Uuid::parse_str(id).ok())
|
||||
.filter(|id| self.workspace.dirs.contains_key(id))
|
||||
.filter_map(|id| {
|
||||
let name = &self.engine.file_structure(&gpx_engine::FileId(id))?.name;
|
||||
let source = sources.iter().find(|(source, _)| source == name);
|
||||
Some((id, source.map(|(_, display)| display.clone())))
|
||||
})
|
||||
.collect();
|
||||
match self.workspace.save_new(&mut self.engine, &made) {
|
||||
Ok(renamed) => outcome = outcome.merge(renamed),
|
||||
@@ -290,8 +265,7 @@ impl Core {
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
self.keep_workspace();
|
||||
self.persist();
|
||||
self.commit(None);
|
||||
outcome
|
||||
}
|
||||
}
|
||||
|
||||
+38
-48
@@ -420,10 +420,23 @@ impl Workspace {
|
||||
outcome
|
||||
}
|
||||
|
||||
/// Opens files from the disk, the first of which is selected. With `preview`, a single file is
|
||||
/// open as a preview (see `open_preview`); otherwise they are open for good, the preview too if
|
||||
/// it is among them.
|
||||
pub fn open(&mut self, engine: &mut Engine, paths: &[PathBuf], preview: bool) -> api::Outcome {
|
||||
if let ([path], true) = (paths, preview) {
|
||||
return self.open_preview(engine, path);
|
||||
}
|
||||
let outcome = self.open_files(engine, paths, true);
|
||||
let ids: Vec<Uuid> = paths.iter().filter_map(|path| self.linked(path)).collect();
|
||||
self.keep(&ids);
|
||||
outcome
|
||||
}
|
||||
|
||||
/// Opens a file as a preview, which is selected: it replaces the file that was the preview,
|
||||
/// which is closed (it has no edits, it would not be the preview anymore). It is the preview
|
||||
/// until it is edited or opened for good (`keep`). A file that is open already is selected.
|
||||
pub fn open_preview(&mut self, engine: &mut Engine, path: &Path) -> api::Outcome {
|
||||
fn open_preview(&mut self, engine: &mut Engine, path: &Path) -> api::Outcome {
|
||||
let path = canonical(path);
|
||||
if self.id_of(&path).is_some() {
|
||||
return self.open_files(engine, &[path], true);
|
||||
@@ -444,11 +457,6 @@ impl Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
/// The open files that are linked to these paths.
|
||||
pub fn ids_at(&self, paths: &[PathBuf]) -> Vec<Uuid> {
|
||||
paths.iter().filter_map(|path| self.linked(path)).collect()
|
||||
}
|
||||
|
||||
/// The link of a file that is the one on the disk, which has the given bytes. `None` if the
|
||||
/// engine does not have the file.
|
||||
fn new_link(&self, engine: &Engine, id: Uuid, path: PathBuf, bytes: &[u8]) -> Option<Link> {
|
||||
@@ -674,10 +682,6 @@ impl Workspace {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn is_taken(&self, path: &Path) -> bool {
|
||||
self.links.values().any(|link| link.path == path)
|
||||
}
|
||||
|
||||
/// Writes files to the disk, replacing what is there: they are the same as their file from
|
||||
/// then on. A file that is not on the disk goes in its folder, or at its path in `paths`, or in
|
||||
/// `folder`, under its name (changed if that is the one of something else), which the file then
|
||||
@@ -694,7 +698,8 @@ impl Workspace {
|
||||
let Some(structure) = engine.file_structure(&FileId(*id)) else {
|
||||
continue;
|
||||
};
|
||||
let in_folder = |dir: &Path| free_path(dir, &structure.name, |p| self.is_taken(p));
|
||||
let in_folder =
|
||||
|dir: &Path| free_path(dir, &structure.name, |p| self.id_of(p).is_some());
|
||||
let path = match (self.links.get(id), paths.get(id), self.dirs.get(id), folder) {
|
||||
(Some(link), ..) => link.path.clone(),
|
||||
(None, Some(path), ..) => canonical(path),
|
||||
@@ -707,25 +712,17 @@ impl Workspace {
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
/// Writes the new files among `files` in their folder, each under the name that is given (the
|
||||
/// name in the file when there is none). A name that is the one of something else is changed
|
||||
/// (`name (2)`). Files that would be called the same, like the parts of a split, are numbered
|
||||
/// from 1 (`name (1)`, `name (2)`). The files are called like the file they are written to.
|
||||
pub fn save_new(
|
||||
&mut self,
|
||||
engine: &mut Engine,
|
||||
files: &[(Uuid, Option<String>)],
|
||||
) -> Result<api::Outcome, String> {
|
||||
let names: Vec<Option<(Uuid, PathBuf, String)>> = files
|
||||
/// Writes the new files among `ids` in their folder, under their name. A name that is the one of
|
||||
/// something else is changed (`name (2)`). Files that would be called the same, like the parts
|
||||
/// of a split, are numbered from 1 (`name (1)`, `name (2)`). The files are called like the file
|
||||
/// they are written to.
|
||||
pub fn save_new(&mut self, engine: &mut Engine, ids: &[Uuid]) -> Result<api::Outcome, String> {
|
||||
let names: Vec<Option<(Uuid, PathBuf, String)>> = ids
|
||||
.iter()
|
||||
.map(|(id, name)| {
|
||||
.map(|id| {
|
||||
let dir = self.dirs.get(id)?.clone();
|
||||
let structure = engine.file_structure(&FileId(*id))?;
|
||||
Some((
|
||||
*id,
|
||||
dir,
|
||||
name.clone().unwrap_or_else(|| structure.name.clone()),
|
||||
))
|
||||
let name = engine.file_structure(&FileId(*id))?.name.clone();
|
||||
Some((*id, dir, name))
|
||||
})
|
||||
.collect();
|
||||
let mut outcome = api::Outcome::failed_none();
|
||||
@@ -741,10 +738,11 @@ impl Workspace {
|
||||
})
|
||||
.count()
|
||||
> 1;
|
||||
let taken = |p: &Path| self.id_of(p).is_some();
|
||||
let path = if together {
|
||||
numbered_path(dir, name, |p| self.is_taken(p))
|
||||
numbered_path(dir, name, taken)
|
||||
} else {
|
||||
free_path(dir, name, |p| self.is_taken(p))
|
||||
free_path(dir, name, taken)
|
||||
};
|
||||
outcome = outcome.merge(self.write_file(engine, *id, &path)?);
|
||||
}
|
||||
@@ -763,20 +761,21 @@ impl Workspace {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The name that a file on the disk can have: the name of a file, without its extension (see
|
||||
/// `file_name`). Others are given to the files that are not on the disk yet when they are
|
||||
/// written.
|
||||
pub fn valid_name(&self, id: Uuid, name: &str) -> String {
|
||||
if self.links.contains_key(&id) {
|
||||
file_name(name)
|
||||
} else {
|
||||
name.to_owned()
|
||||
/// The name that a file can be given. A file on the disk is called like its file: the name is
|
||||
/// one that a file can have (see `file_name`), and not the one of another file next to it
|
||||
/// (`NAME_TAKEN`). The other files get theirs when they are written.
|
||||
pub fn name_for(&self, id: Uuid, name: &str) -> Result<String, String> {
|
||||
if !self.links.contains_key(&id) {
|
||||
return Ok(name.to_owned());
|
||||
}
|
||||
let name = file_name(name);
|
||||
self.renamed_path(id, &name)?;
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
/// Where a file on the disk goes when it is called `name`: next to where it is, with the same
|
||||
/// extension. `None` if it is there already, an error if something else is.
|
||||
pub fn renamed_path(&self, id: Uuid, name: &str) -> Result<Option<PathBuf>, String> {
|
||||
fn renamed_path(&self, id: Uuid, name: &str) -> Result<Option<PathBuf>, String> {
|
||||
let Some(link) = self.links.get(&id) else {
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -848,15 +847,6 @@ impl Workspace {
|
||||
(renamed, errors)
|
||||
}
|
||||
|
||||
/// The name of a file as the user knows it: the name of the file on the disk, without its
|
||||
/// extension, or else the name in the file.
|
||||
pub fn display_name(&self, engine: &Engine, id: Uuid) -> Option<String> {
|
||||
match self.links.get(&id) {
|
||||
Some(link) => Some(stem(&link.path)),
|
||||
None => Some(engine.file_structure(&FileId(id))?.name.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a file to a path of the user's choice, which is the place of the file from then on.
|
||||
pub fn save_as(
|
||||
&mut self,
|
||||
|
||||
Reference in new issue
Block a user