mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
refactor: eliminate the old kitty graphics protocol
This commit is contained in:
parent
31ee5ab546
commit
d2963b23a9
8 changed files with 188 additions and 264 deletions
|
|
@ -7,7 +7,7 @@ use tracing::warn;
|
|||
use yazi_config::PREVIEW;
|
||||
use yazi_shared::{env_exists, RoCell};
|
||||
|
||||
use super::{Iterm2, Kitty, KittyOld};
|
||||
use super::{Iterm2, Kitty};
|
||||
use crate::{ueberzug::Ueberzug, Sixel, TMUX};
|
||||
|
||||
static IMAGE_SHOWN: AtomicBool = AtomicBool::new(false);
|
||||
|
|
@ -18,7 +18,6 @@ static UEBERZUG: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCe
|
|||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Adaptor {
|
||||
Kitty,
|
||||
KittyOld,
|
||||
Iterm2,
|
||||
Sixel,
|
||||
|
||||
|
|
@ -86,7 +85,7 @@ impl Adaptor {
|
|||
Emulator::Kitty => vec![Self::Kitty],
|
||||
Emulator::Konsole => vec![Self::Kitty, Self::Iterm2, Self::Sixel],
|
||||
Emulator::Iterm2 => vec![Self::Iterm2, Self::Sixel],
|
||||
Emulator::WezTerm => vec![Self::KittyOld, Self::Iterm2, Self::Sixel],
|
||||
Emulator::WezTerm => vec![Self::Iterm2, Self::Sixel],
|
||||
Emulator::Foot => vec![Self::Sixel],
|
||||
Emulator::BlackBox => vec![Self::Sixel],
|
||||
Emulator::VSCode => vec![Self::Sixel],
|
||||
|
|
@ -100,9 +99,6 @@ impl Adaptor {
|
|||
if env_exists("ZELLIJ_SESSION_NAME") {
|
||||
protocols.retain(|p| *p == Self::Sixel);
|
||||
}
|
||||
if *TMUX && protocols.len() > 1 {
|
||||
protocols.retain(|p| *p != Self::KittyOld);
|
||||
}
|
||||
if let Some(p) = protocols.first() {
|
||||
return *p;
|
||||
}
|
||||
|
|
@ -152,7 +148,6 @@ impl ToString for Adaptor {
|
|||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Kitty => "kitty",
|
||||
Self::KittyOld => "kitty",
|
||||
Self::Iterm2 => "iterm2",
|
||||
Self::Sixel => "sixel",
|
||||
Self::X11 => "x11",
|
||||
|
|
@ -179,7 +174,6 @@ impl Adaptor {
|
|||
|
||||
match self {
|
||||
Self::Kitty => Kitty::image_show(path, rect).await,
|
||||
Self::KittyOld => KittyOld::image_show(path, rect).await,
|
||||
Self::Iterm2 => Iterm2::image_show(path, rect).await,
|
||||
Self::Sixel => Sixel::image_show(path, rect).await,
|
||||
_ => Ok(if let Some(tx) = &*UEBERZUG {
|
||||
|
|
@ -195,7 +189,6 @@ impl Adaptor {
|
|||
|
||||
match self {
|
||||
Self::Kitty => Kitty::image_hide(rect),
|
||||
Self::KittyOld => KittyOld::image_hide(),
|
||||
Self::Iterm2 => Iterm2::image_hide(rect),
|
||||
Self::Sixel => Sixel::image_hide(rect),
|
||||
_ => Ok(if let Some(tx) = &*UEBERZUG {
|
||||
|
|
@ -206,6 +199,6 @@ impl Adaptor {
|
|||
|
||||
#[inline]
|
||||
pub(super) fn needs_ueberzug(self) -> bool {
|
||||
!matches!(self, Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel)
|
||||
!matches!(self, Self::Kitty | Self::Iterm2 | Self::Sixel)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
use std::{io::{stdout, Write}, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use image::DynamicImage;
|
||||
use ratatui::prelude::Rect;
|
||||
use yazi_shared::Term;
|
||||
|
||||
use super::image::Image;
|
||||
use crate::{CLOSE, ESCAPE, START};
|
||||
|
||||
pub(super) struct KittyOld;
|
||||
|
||||
impl KittyOld {
|
||||
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<()> {
|
||||
let img = Image::downscale(path, (rect.width, rect.height)).await?;
|
||||
let b = Self::encode(img).await?;
|
||||
|
||||
Self::image_hide()?;
|
||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn image_hide() -> Result<()> {
|
||||
let mut stdout = stdout().lock();
|
||||
stdout.write_all(format!("{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?;
|
||||
stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
|
||||
fn output(raw: &[u8], format: u8, size: (u32, u32)) -> Result<Vec<u8>> {
|
||||
let b64 = general_purpose::STANDARD.encode(raw).chars().collect::<Vec<_>>();
|
||||
|
||||
let mut it = b64.chunks(4096).peekable();
|
||||
let mut buf = Vec::with_capacity(b64.len() + it.len() * 50);
|
||||
if let Some(first) = it.next() {
|
||||
write!(
|
||||
buf,
|
||||
"{}_Gq=1,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}",
|
||||
START,
|
||||
format,
|
||||
size.0,
|
||||
size.1,
|
||||
it.peek().is_some() as u8,
|
||||
first.iter().collect::<String>(),
|
||||
ESCAPE,
|
||||
CLOSE
|
||||
)?;
|
||||
}
|
||||
|
||||
while let Some(chunk) = it.next() {
|
||||
write!(
|
||||
buf,
|
||||
"{}_Gm={};{}{}\\{}",
|
||||
START,
|
||||
it.peek().is_some() as u8,
|
||||
chunk.iter().collect::<String>(),
|
||||
ESCAPE,
|
||||
CLOSE
|
||||
)?;
|
||||
}
|
||||
|
||||
buf.write_all(CLOSE.as_bytes())?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
let size = (img.width(), img.height());
|
||||
tokio::task::spawn_blocking(move || match img {
|
||||
DynamicImage::ImageRgb8(v) => output(v.as_raw(), 24, size),
|
||||
DynamicImage::ImageRgba8(v) => output(v.as_raw(), 32, size),
|
||||
v => output(v.to_rgb8().as_raw(), 24, size),
|
||||
})
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
|
@ -4,14 +4,12 @@ mod adaptor;
|
|||
mod image;
|
||||
mod iterm2;
|
||||
mod kitty;
|
||||
mod kitty_old;
|
||||
mod sixel;
|
||||
mod ueberzug;
|
||||
|
||||
use adaptor::*;
|
||||
use iterm2::*;
|
||||
use kitty::*;
|
||||
use kitty_old::*;
|
||||
use sixel::*;
|
||||
use yazi_shared::{env_exists, RoCell};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{env, ffi::OsString, fs, path::PathBuf, process};
|
||||
use std::{ffi::OsString, fs, path::PathBuf, process};
|
||||
|
||||
use clap::Parser;
|
||||
use yazi_shared::expand_path;
|
||||
use yazi_shared::{current_cwd, expand_path};
|
||||
|
||||
use super::cli::Args;
|
||||
use crate::{Xdg, PREVIEW};
|
||||
|
|
@ -21,7 +21,7 @@ impl Boot {
|
|||
fn parse_entry(entry: Option<PathBuf>) -> (PathBuf, Option<OsString>) {
|
||||
let entry = match entry {
|
||||
Some(p) => expand_path(p),
|
||||
None => return (env::current_dir().unwrap(), None),
|
||||
None => return (current_cwd().unwrap(), None),
|
||||
};
|
||||
|
||||
let parent = entry.parent();
|
||||
|
|
|
|||
|
|
@ -1,171 +1,2 @@
|
|||
use std::{borrow::Cow, env, ffi::OsString, path::{Component, Path, PathBuf, MAIN_SEPARATOR}};
|
||||
|
||||
use tokio::fs;
|
||||
|
||||
use crate::Url;
|
||||
|
||||
fn _expand_path(p: &Path) -> PathBuf {
|
||||
// ${HOME} or $HOME
|
||||
#[cfg(unix)]
|
||||
let re = regex::Regex::new(r"\$(?:\{([^}]+)\}|([a-zA-Z\d_]+))").unwrap();
|
||||
|
||||
// %USERPROFILE%
|
||||
#[cfg(windows)]
|
||||
let re = regex::Regex::new(r"%([^%]+)%").unwrap();
|
||||
|
||||
let s = p.to_string_lossy();
|
||||
let s = re.replace_all(&s, |caps: ®ex::Captures| {
|
||||
let name = caps.get(2).or_else(|| caps.get(1)).unwrap();
|
||||
env::var(name.as_str()).unwrap_or_else(|_| caps.get(0).unwrap().as_str().to_owned())
|
||||
});
|
||||
|
||||
let p = Path::new(s.as_ref());
|
||||
if let Ok(rest) = p.strip_prefix("~") {
|
||||
#[cfg(unix)]
|
||||
let home = env::var_os("HOME");
|
||||
#[cfg(windows)]
|
||||
let home = env::var_os("USERPROFILE");
|
||||
|
||||
return if let Some(p) = home { PathBuf::from(p).join(rest) } else { rest.to_path_buf() };
|
||||
}
|
||||
|
||||
if p.is_absolute() {
|
||||
return p.to_path_buf();
|
||||
}
|
||||
env::current_dir().map_or_else(|_| p.to_path_buf(), |c| c.join(p))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn expand_path(p: impl AsRef<Path>) -> PathBuf { _expand_path(p.as_ref()) }
|
||||
|
||||
#[inline]
|
||||
pub fn ends_with_slash(p: &Path) -> bool {
|
||||
// TODO: uncomment this when Rust 1.74 is released
|
||||
// let b = p.as_os_str().as_encoded_bytes();
|
||||
// if let [.., last] = b { *last == MAIN_SEPARATOR as u8 } else { false }
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
let b = p.as_os_str().as_bytes();
|
||||
if let [.., last] = b { *last == MAIN_SEPARATOR as u8 } else { false }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let s = p.to_string_lossy();
|
||||
let b = s.as_bytes();
|
||||
if let [.., last] = b { *last == MAIN_SEPARATOR as u8 } else { false }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn unique_path(mut p: Url) -> Url {
|
||||
let Some(stem) = p.file_stem().map(|s| s.to_owned()) else {
|
||||
return p;
|
||||
};
|
||||
|
||||
let ext = p
|
||||
.extension()
|
||||
.map(|s| {
|
||||
let mut n = OsString::with_capacity(s.len() + 1);
|
||||
n.push(".");
|
||||
n.push(s);
|
||||
n
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut i = 0;
|
||||
while fs::symlink_metadata(&p).await.is_ok() {
|
||||
i += 1;
|
||||
|
||||
let mut name = OsString::with_capacity(stem.len() + ext.len() + 5);
|
||||
name.push(&stem);
|
||||
name.push(format!("_{i}"));
|
||||
if !ext.is_empty() {
|
||||
name.push(&ext);
|
||||
}
|
||||
|
||||
p.set_file_name(name);
|
||||
}
|
||||
p
|
||||
}
|
||||
|
||||
// Parameters
|
||||
// * `path`: The absolute path(contains no `/./`) to get relative path.
|
||||
// * `root`: The absolute path(contains no `/./`) to be compared.
|
||||
//
|
||||
// Return
|
||||
// * Unix: The relative format to `root` of `path`.
|
||||
// * Windows: The relative format to `root` of `path`; or `path` itself when
|
||||
// `path` and `root` are both under different disk drives.
|
||||
pub fn path_relative_to<'a>(path: &'a Path, root: &Path) -> Cow<'a, Path> {
|
||||
assert!(path.is_absolute());
|
||||
assert!(root.is_absolute());
|
||||
let mut p_comps = path.components();
|
||||
let mut r_comps = root.components();
|
||||
|
||||
// 1. Ensure that the two paths have the same prefix.
|
||||
// 2. Strips any common prefix the two paths do have.
|
||||
//
|
||||
// NOTE:
|
||||
// Prefixes are platform dependent,
|
||||
// but different prefixes would for example indicate paths for different drives
|
||||
// on Windows.
|
||||
let (p_head, r_head) = loop {
|
||||
use std::path::Component::*;
|
||||
match (p_comps.next(), r_comps.next()) {
|
||||
(Some(RootDir), Some(RootDir)) => (),
|
||||
(Some(Prefix(a)), Some(Prefix(b))) if a == b => (),
|
||||
(Some(Prefix(_) | RootDir), _) | (_, Some(Prefix(_) | RootDir)) => {
|
||||
return Cow::from(path);
|
||||
}
|
||||
(None, None) => break (None, None),
|
||||
(a, b) if a != b => break (a, b),
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
|
||||
let p_comps = p_head.into_iter().chain(p_comps);
|
||||
let walk_up = r_head.into_iter().chain(r_comps).map(|_| Component::ParentDir);
|
||||
|
||||
let mut buf = PathBuf::new();
|
||||
buf.extend(walk_up);
|
||||
buf.extend(p_comps);
|
||||
|
||||
Cow::from(buf)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{borrow::Cow, path::Path};
|
||||
|
||||
use super::path_relative_to;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_path_relative_to() {
|
||||
fn assert(path: &str, root: &str, res: &str) {
|
||||
assert_eq!(path_relative_to(Path::new(path), Path::new(root)), Cow::Borrowed(Path::new(res)));
|
||||
}
|
||||
|
||||
assert("/a/b", "/a/b/c", "../");
|
||||
assert("/a/b/c", "/a/b", "c");
|
||||
assert("/a/b/c", "/a/b/d", "../c");
|
||||
assert("/a", "/a/b/c", "../../");
|
||||
assert("/a/a/b", "/a/b/b", "../../a/b");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_path_relative_to() {
|
||||
fn assert(path: &str, root: &str, res: &str) {
|
||||
assert_eq!(path_relative_to(Path::new(path), Path::new(root)), Cow::Borrowed(Path::new(res)));
|
||||
}
|
||||
|
||||
assert("C:\\a\\b", "C:\\a\\b\\c", "..\\");
|
||||
assert("C:\\a\\b\\c", "C:\\a\\b", "c");
|
||||
assert("C:\\a\\b\\c", "C:\\a\\b\\d", "..\\c");
|
||||
assert("C:\\a", "C:\\a\\b\\c", "..\\..\\");
|
||||
assert("C:\\a\\a\\b", "C:\\a\\b\\b", "..\\..\\a\\b");
|
||||
}
|
||||
}
|
||||
pub fn env_exists(name: &str) -> bool { std::env::var_os(name).is_some_and(|s| !s.is_empty()) }
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ use std::{collections::VecDeque, path::{Path, PathBuf}};
|
|||
use anyhow::Result;
|
||||
use tokio::{fs, io, select, sync::{mpsc, oneshot}, time};
|
||||
|
||||
#[inline]
|
||||
pub fn env_exists(name: &str) -> bool { std::env::var_os(name).is_some_and(|s| !s.is_empty()) }
|
||||
|
||||
pub async fn calculate_size(path: &Path) -> u64 {
|
||||
let mut total = 0;
|
||||
let mut stack = VecDeque::from([path.to_path_buf()]);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ mod fns;
|
|||
mod fs;
|
||||
mod mime;
|
||||
mod natsort;
|
||||
mod path;
|
||||
mod ro_cell;
|
||||
mod term;
|
||||
mod throttle;
|
||||
|
|
@ -24,6 +25,7 @@ pub use fns::*;
|
|||
pub use fs::*;
|
||||
pub use mime::*;
|
||||
pub use natsort::*;
|
||||
pub use path::*;
|
||||
pub use ro_cell::*;
|
||||
pub use term::*;
|
||||
pub use throttle::*;
|
||||
|
|
|
|||
179
yazi-shared/src/path.rs
Normal file
179
yazi-shared/src/path.rs
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
use std::{borrow::Cow, env, ffi::OsString, path::{Component, Path, PathBuf, MAIN_SEPARATOR}};
|
||||
|
||||
use tokio::fs;
|
||||
|
||||
use crate::Url;
|
||||
|
||||
#[inline]
|
||||
pub fn current_cwd() -> Option<PathBuf> {
|
||||
env::var_os("PWD")
|
||||
.map(PathBuf::from)
|
||||
.filter(|p| p.is_absolute())
|
||||
.or_else(|| env::current_dir().ok())
|
||||
}
|
||||
|
||||
fn _expand_path(p: &Path) -> PathBuf {
|
||||
// ${HOME} or $HOME
|
||||
#[cfg(unix)]
|
||||
let re = regex::Regex::new(r"\$(?:\{([^}]+)\}|([a-zA-Z\d_]+))").unwrap();
|
||||
|
||||
// %USERPROFILE%
|
||||
#[cfg(windows)]
|
||||
let re = regex::Regex::new(r"%([^%]+)%").unwrap();
|
||||
|
||||
let s = p.to_string_lossy();
|
||||
let s = re.replace_all(&s, |caps: ®ex::Captures| {
|
||||
let name = caps.get(2).or_else(|| caps.get(1)).unwrap();
|
||||
env::var(name.as_str()).unwrap_or_else(|_| caps.get(0).unwrap().as_str().to_owned())
|
||||
});
|
||||
|
||||
let p = Path::new(s.as_ref());
|
||||
if let Ok(rest) = p.strip_prefix("~") {
|
||||
#[cfg(unix)]
|
||||
let home = env::var_os("HOME");
|
||||
#[cfg(windows)]
|
||||
let home = env::var_os("USERPROFILE");
|
||||
|
||||
return if let Some(p) = home { PathBuf::from(p).join(rest) } else { rest.to_path_buf() };
|
||||
}
|
||||
|
||||
if p.is_absolute() {
|
||||
return p.to_path_buf();
|
||||
}
|
||||
current_cwd().map_or_else(|| p.to_path_buf(), |c| c.join(p))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn expand_path(p: impl AsRef<Path>) -> PathBuf { _expand_path(p.as_ref()) }
|
||||
|
||||
#[inline]
|
||||
pub fn ends_with_slash(p: &Path) -> bool {
|
||||
// TODO: uncomment this when Rust 1.74 is released
|
||||
// let b = p.as_os_str().as_encoded_bytes();
|
||||
// if let [.., last] = b { *last == MAIN_SEPARATOR as u8 } else { false }
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
let b = p.as_os_str().as_bytes();
|
||||
if let [.., last] = b { *last == MAIN_SEPARATOR as u8 } else { false }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let s = p.to_string_lossy();
|
||||
let b = s.as_bytes();
|
||||
if let [.., last] = b { *last == MAIN_SEPARATOR as u8 } else { false }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn unique_path(mut p: Url) -> Url {
|
||||
let Some(stem) = p.file_stem().map(|s| s.to_owned()) else {
|
||||
return p;
|
||||
};
|
||||
|
||||
let ext = p
|
||||
.extension()
|
||||
.map(|s| {
|
||||
let mut n = OsString::with_capacity(s.len() + 1);
|
||||
n.push(".");
|
||||
n.push(s);
|
||||
n
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut i = 0;
|
||||
while fs::symlink_metadata(&p).await.is_ok() {
|
||||
i += 1;
|
||||
|
||||
let mut name = OsString::with_capacity(stem.len() + ext.len() + 5);
|
||||
name.push(&stem);
|
||||
name.push(format!("_{i}"));
|
||||
if !ext.is_empty() {
|
||||
name.push(&ext);
|
||||
}
|
||||
|
||||
p.set_file_name(name);
|
||||
}
|
||||
p
|
||||
}
|
||||
|
||||
// Parameters
|
||||
// * `path`: The absolute path(contains no `/./`) to get relative path.
|
||||
// * `root`: The absolute path(contains no `/./`) to be compared.
|
||||
//
|
||||
// Return
|
||||
// * Unix: The relative format to `root` of `path`.
|
||||
// * Windows: The relative format to `root` of `path`; or `path` itself when
|
||||
// `path` and `root` are both under different disk drives.
|
||||
pub fn path_relative_to<'a>(path: &'a Path, root: &Path) -> Cow<'a, Path> {
|
||||
assert!(path.is_absolute());
|
||||
assert!(root.is_absolute());
|
||||
let mut p_comps = path.components();
|
||||
let mut r_comps = root.components();
|
||||
|
||||
// 1. Ensure that the two paths have the same prefix.
|
||||
// 2. Strips any common prefix the two paths do have.
|
||||
//
|
||||
// NOTE:
|
||||
// Prefixes are platform dependent,
|
||||
// but different prefixes would for example indicate paths for different drives
|
||||
// on Windows.
|
||||
let (p_head, r_head) = loop {
|
||||
use std::path::Component::*;
|
||||
match (p_comps.next(), r_comps.next()) {
|
||||
(Some(RootDir), Some(RootDir)) => (),
|
||||
(Some(Prefix(a)), Some(Prefix(b))) if a == b => (),
|
||||
(Some(Prefix(_) | RootDir), _) | (_, Some(Prefix(_) | RootDir)) => {
|
||||
return Cow::from(path);
|
||||
}
|
||||
(None, None) => break (None, None),
|
||||
(a, b) if a != b => break (a, b),
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
|
||||
let p_comps = p_head.into_iter().chain(p_comps);
|
||||
let walk_up = r_head.into_iter().chain(r_comps).map(|_| Component::ParentDir);
|
||||
|
||||
let mut buf = PathBuf::new();
|
||||
buf.extend(walk_up);
|
||||
buf.extend(p_comps);
|
||||
|
||||
Cow::from(buf)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{borrow::Cow, path::Path};
|
||||
|
||||
use super::path_relative_to;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_path_relative_to() {
|
||||
fn assert(path: &str, root: &str, res: &str) {
|
||||
assert_eq!(path_relative_to(Path::new(path), Path::new(root)), Cow::Borrowed(Path::new(res)));
|
||||
}
|
||||
|
||||
assert("/a/b", "/a/b/c", "../");
|
||||
assert("/a/b/c", "/a/b", "c");
|
||||
assert("/a/b/c", "/a/b/d", "../c");
|
||||
assert("/a", "/a/b/c", "../../");
|
||||
assert("/a/a/b", "/a/b/b", "../../a/b");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_path_relative_to() {
|
||||
fn assert(path: &str, root: &str, res: &str) {
|
||||
assert_eq!(path_relative_to(Path::new(path), Path::new(root)), Cow::Borrowed(Path::new(res)));
|
||||
}
|
||||
|
||||
assert("C:\\a\\b", "C:\\a\\b\\c", "..\\");
|
||||
assert("C:\\a\\b\\c", "C:\\a\\b", "c");
|
||||
assert("C:\\a\\b\\c", "C:\\a\\b\\d", "..\\c");
|
||||
assert("C:\\a", "C:\\a\\b\\c", "..\\..\\");
|
||||
assert("C:\\a\\a\\b", "C:\\a\\b\\b", "..\\..\\a\\b");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue