mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-25 05:29:39 +00:00
file_util: move path handling functions from ui module
We'll need this kind of functions in the library crate to parse user file patterns.
This commit is contained in:
parent
756c4fedb6
commit
689332aedd
4 changed files with 74 additions and 73 deletions
|
@ -13,10 +13,59 @@
|
|||
// limitations under the License.
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::iter;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
use tempfile::{NamedTempFile, PersistError};
|
||||
|
||||
/// Turns the given `to` path into relative path starting from the `from` path.
|
||||
///
|
||||
/// Both `from` and `to` paths are supposed to be absolute and normalized in the
|
||||
/// same manner.
|
||||
pub fn relative_path(from: &Path, to: &Path) -> PathBuf {
|
||||
// Find common prefix.
|
||||
for (i, base) in from.ancestors().enumerate() {
|
||||
if let Ok(suffix) = to.strip_prefix(base) {
|
||||
if i == 0 && suffix.as_os_str().is_empty() {
|
||||
return ".".into();
|
||||
} else {
|
||||
let mut result = PathBuf::from_iter(iter::repeat("..").take(i));
|
||||
result.push(suffix);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No common prefix found. Return the original (absolute) path.
|
||||
to.to_owned()
|
||||
}
|
||||
|
||||
/// Consumes as much `..` and `.` as possible without considering symlinks.
|
||||
pub fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut result = PathBuf::new();
|
||||
for c in path.components() {
|
||||
match c {
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir
|
||||
if matches!(result.components().next_back(), Some(Component::Normal(_))) =>
|
||||
{
|
||||
// Do not pop ".."
|
||||
let popped = result.pop();
|
||||
assert!(popped);
|
||||
}
|
||||
_ => {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.as_os_str().is_empty() {
|
||||
".".into()
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// Like NamedTempFile::persist(), but also succeeds if the target already
|
||||
// exists.
|
||||
pub fn persist_content_addressed_temp_file<P: AsRef<Path>>(
|
||||
|
@ -44,6 +93,20 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::testutils;
|
||||
|
||||
#[test]
|
||||
fn normalize_too_many_dot_dot() {
|
||||
assert_eq!(normalize_path(Path::new("foo/..")), Path::new("."));
|
||||
assert_eq!(normalize_path(Path::new("foo/../..")), Path::new(".."));
|
||||
assert_eq!(
|
||||
normalize_path(Path::new("foo/../../..")),
|
||||
Path::new("../..")
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_path(Path::new("foo/../../../bar/baz/..")),
|
||||
Path::new("../../bar")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_persist_no_existing_file() {
|
||||
let temp_dir = testutils::new_temp_dir();
|
||||
|
|
|
@ -41,13 +41,12 @@ use jujutsu_lib::working_copy::{
|
|||
CheckoutStats, LockedWorkingCopy, ResetError, SnapshotError, WorkingCopy,
|
||||
};
|
||||
use jujutsu_lib::workspace::{Workspace, WorkspaceInitError, WorkspaceLoadError};
|
||||
use jujutsu_lib::{dag_walk, git, revset};
|
||||
use jujutsu_lib::{dag_walk, file_util, git, revset};
|
||||
|
||||
use crate::config::read_config;
|
||||
use crate::diff_edit::DiffEditError;
|
||||
use crate::formatter::Formatter;
|
||||
use crate::templater::TemplateFormatter;
|
||||
use crate::ui;
|
||||
use crate::ui::{ColorChoice, FilePathParseError, Ui};
|
||||
|
||||
pub enum CommandError {
|
||||
|
@ -496,7 +495,7 @@ impl WorkspaceCommandHelper {
|
|||
}
|
||||
|
||||
pub fn format_file_path(&self, file: &RepoPath) -> String {
|
||||
ui::relative_path(&self.cwd, &file.to_fs_path(self.workspace_root()))
|
||||
file_util::relative_path(&self.cwd, &file.to_fs_path(self.workspace_root()))
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
|
|
|
@ -46,7 +46,7 @@ use jujutsu_lib::store::Store;
|
|||
use jujutsu_lib::tree::{merge_trees, Tree, TreeDiffIterator};
|
||||
use jujutsu_lib::view::View;
|
||||
use jujutsu_lib::workspace::Workspace;
|
||||
use jujutsu_lib::{conflicts, diff, files, git, revset, tree};
|
||||
use jujutsu_lib::{conflicts, diff, file_util, files, git, revset, tree};
|
||||
use maplit::{hashmap, hashset};
|
||||
use pest::Parser;
|
||||
|
||||
|
@ -60,7 +60,6 @@ use crate::formatter::Formatter;
|
|||
use crate::graphlog::{AsciiGraphDrawer, Edge};
|
||||
use crate::template_parser::TemplateParser;
|
||||
use crate::templater::Template;
|
||||
use crate::ui;
|
||||
use crate::ui::Ui;
|
||||
|
||||
#[derive(clap::Parser, Clone, Debug)]
|
||||
|
@ -1122,7 +1121,7 @@ Set `ui.allow-init-native` to allow initializing a repo with the native backend.
|
|||
Workspace::init_local(ui.settings(), &wc_path)?;
|
||||
};
|
||||
let cwd = ui.cwd().canonicalize().unwrap();
|
||||
let relative_wc_path = ui::relative_path(&cwd, &wc_path);
|
||||
let relative_wc_path = file_util::relative_path(&cwd, &wc_path);
|
||||
writeln!(ui, "Initialized repo in \"{}\"", relative_wc_path.display())?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3797,7 +3796,8 @@ fn cmd_workspace_add(
|
|||
writeln!(
|
||||
ui,
|
||||
"Created workspace in \"{}\"",
|
||||
ui::relative_path(old_workspace_command.workspace_root(), &destination_path).display()
|
||||
file_util::relative_path(old_workspace_command.workspace_root(), &destination_path)
|
||||
.display()
|
||||
)?;
|
||||
|
||||
let mut new_workspace_command = WorkspaceCommandHelper::new(
|
||||
|
|
69
src/ui.rs
69
src/ui.rs
|
@ -15,9 +15,10 @@
|
|||
use std::io::{Stderr, Stdout, Write};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, io, iter};
|
||||
use std::{fmt, io};
|
||||
|
||||
use atty::Stream;
|
||||
use jujutsu_lib::file_util;
|
||||
use jujutsu_lib::repo_path::{RepoPath, RepoPathComponent};
|
||||
use jujutsu_lib::settings::UserSettings;
|
||||
|
||||
|
@ -176,8 +177,8 @@ impl Ui {
|
|||
wc_path: &Path,
|
||||
input: &str,
|
||||
) -> Result<RepoPath, FilePathParseError> {
|
||||
let abs_input_path = normalize_path(&self.cwd.join(input));
|
||||
let repo_relative_path = relative_path(wc_path, &abs_input_path);
|
||||
let abs_input_path = file_util::normalize_path(&self.cwd.join(input));
|
||||
let repo_relative_path = file_util::relative_path(wc_path, &abs_input_path);
|
||||
if repo_relative_path == Path::new(".") {
|
||||
return Ok(RepoPath::root());
|
||||
}
|
||||
|
@ -201,54 +202,6 @@ pub enum FilePathParseError {
|
|||
InputNotInRepo(String),
|
||||
}
|
||||
|
||||
/// Turns the given `to` path into relative path starting from the `from` path.
|
||||
///
|
||||
/// Both `from` and `to` paths are supposed to be absolute and normalized in the
|
||||
/// same manner.
|
||||
pub fn relative_path(from: &Path, to: &Path) -> PathBuf {
|
||||
// Find common prefix.
|
||||
for (i, base) in from.ancestors().enumerate() {
|
||||
if let Ok(suffix) = to.strip_prefix(base) {
|
||||
if i == 0 && suffix.as_os_str().is_empty() {
|
||||
return ".".into();
|
||||
} else {
|
||||
let mut result = PathBuf::from_iter(iter::repeat("..").take(i));
|
||||
result.push(suffix);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No common prefix found. Return the original (absolute) path.
|
||||
to.to_owned()
|
||||
}
|
||||
|
||||
/// Consumes as much `..` and `.` as possible without considering symlinks.
|
||||
fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut result = PathBuf::new();
|
||||
for c in path.components() {
|
||||
match c {
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir
|
||||
if matches!(result.components().next_back(), Some(Component::Normal(_))) =>
|
||||
{
|
||||
// Do not pop ".."
|
||||
let popped = result.pop();
|
||||
assert!(popped);
|
||||
}
|
||||
_ => {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.as_os_str().is_empty() {
|
||||
".".into()
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use jujutsu_lib::testutils;
|
||||
|
@ -355,18 +308,4 @@ mod tests {
|
|||
Ok(RepoPath::from_internal_string("dir/file"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_too_many_dot_dot() {
|
||||
assert_eq!(normalize_path(Path::new("foo/..")), Path::new("."));
|
||||
assert_eq!(normalize_path(Path::new("foo/../..")), Path::new(".."));
|
||||
assert_eq!(
|
||||
normalize_path(Path::new("foo/../../..")),
|
||||
Path::new("../..")
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_path(Path::new("foo/../../../bar/baz/..")),
|
||||
Path::new("../../bar")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue