From 464503cb16bf20e07bf62bd8b39d795f0bdd96e1 Mon Sep 17 00:00:00 2001 From: Vince Buffalo Date: Tue, 5 Sep 2023 14:24:57 -0700 Subject: [PATCH] new move command --- src/lib/data.rs | 4 ++-- src/lib/project.rs | 51 ++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 10 ++++++++- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/lib/data.rs b/src/lib/data.rs index 1ee1148..caba78a 100644 --- a/src/lib/data.rs +++ b/src/lib/data.rs @@ -1,6 +1,6 @@ use std::path::{PathBuf,Path}; use anyhow::{anyhow,Result}; -use std::fs::{metadata}; +use std::fs::metadata; use serde_derive::{Serialize,Deserialize}; use serde; use crate::lib::data::serde::{Serializer,Deserializer}; @@ -668,7 +668,7 @@ impl DataCollection { match data_file { None => Err(anyhow!("Cannot untrack data file '{}' since it was never added to\ the data manifest.", filepath)), - Some(data_file) => data_file.set_untracked() + Some(file) => file.set_untracked() } } diff --git a/src/lib/project.rs b/src/lib/project.rs index 1813975..9875bc9 100644 --- a/src/lib/project.rs +++ b/src/lib/project.rs @@ -1,4 +1,4 @@ -use std::fs::{File,metadata,canonicalize}; +use std::fs::{File,metadata,canonicalize,rename}; use anyhow::{anyhow,Result,Context}; use serde_yaml; use serde_derive::{Serialize,Deserialize}; @@ -252,9 +252,9 @@ impl Project { } pub fn relative_path(&self, path: &Path) -> Result { - let absolute_path = canonicalize(path)?; + let absolute_path = canonicalize(path).context(format!("Failed to canonicalize path '{}'.", path.to_string_lossy()))?; //ensure_directory(&absolute_path)?; - let path_context = canonicalize(self.path_context())?; + let path_context = canonicalize(self.path_context()).context(format!("Failed to canonicalize path '{}'.", path.to_string_lossy()))?; // Compute relative path directly using strip_prefix match absolute_path.strip_prefix(&path_context) { @@ -264,7 +264,11 @@ impl Project { } pub fn relative_path_string(&self, path: &Path) -> Result { - Ok(self.relative_path(path)?.to_string_lossy().to_string()) + if !path.exists() { + Err(anyhow!("Path '{}' does not exist.", path.to_string_lossy())) + } else { + Ok(self.relative_path(path)?.to_string_lossy().to_string()) + } } pub async fn status(&mut self, include_remotes: bool, all: bool) -> Result<()> { @@ -385,6 +389,35 @@ impl Project { Ok(()) } + // Move a file within the project. + // + // Note: file moving is done within relatively higher project-level API. + // The reason why is that we need to access Project::relative_path_string() for + // both the source *and* destination; the latter does not exist until after the file + // has been successfully moved. So the updating is all done on the DataFile + // directly, since lower interfaces cannot access the relative path. + pub async fn mv(&mut self, source: &str, destination: &str) -> Result<()> { + let source_path = self.relative_path_string(Path::new(source))?; + if let Some(file) = self.data.files.remove(&source_path) { + // move the actual file + rename(source, destination).context("Error encountered when moving file.")?; + + // update the relative path + let relative_destination = self.relative_path_string(Path::new(destination))?; + + // modify the DataFile + let mut new_file = file.clone(); + new_file.path = relative_destination; + + // insert it back into the map with the new key + self.data.files.insert(destination.to_string(), new_file); + + self.save() + } else { + Err(anyhow!("Cannot move file '{}' with 'sdf mv' since it is not in the manifest.", source)) + } + } + pub async fn get(&mut self, url: &str, filename: Option<&str>, overwrite: bool) -> Result<()> { let mut downloads = Downloads::new(); @@ -475,11 +508,11 @@ impl Project { } let num_skipped = skipped.len(); println!("{} URLs found in '{}.'\n\ - {} files were downloaded, {} added to manifest ({} were already registered).\n\ - {} files were skipped because they existed (and --overwrite was no specified).", - num_lines, filename, - urls.len(), num_added, num_already_registered, - num_skipped); + {} files were downloaded, {} added to manifest ({} were already registered).\n\ + {} files were skipped because they existed (and --overwrite was no specified).", + num_lines, filename, + urls.len(), num_added, num_already_registered, + num_skipped); self.save()?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index e36cfc2..4aa9e4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -182,7 +182,11 @@ enum Commands { /// The file to track with remote. filename: String }, - + Mv { + /// Move or rename a file on the file system and in the manifest. + source: String, + destination: String + }, #[structopt(name = "push")] /// Push all tracked files to remote. Push { @@ -301,6 +305,10 @@ async fn run() -> Result<()> { let mut proj = Project::new()?; proj.untrack(filename) }, + Some(Commands::Mv { source, destination }) => { + let mut proj = Project::new()?; + proj.mv(source, destination).await + }, Some(Commands::Push { overwrite }) => { let mut proj = Project::new()?; proj.push(*overwrite).await