Skip to content

Commit a21bb00

Browse files
committed
more util funcs
1 parent f4c46cd commit a21bb00

File tree

3 files changed

+115
-30
lines changed

3 files changed

+115
-30
lines changed

crates/core/src/backend/ignore.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::{
3131
ReadSource, ReadSourceEntry, ReadSourceOpen,
3232
},
3333
error::{ErrorKind, RusticError, RusticResult},
34+
util::path_to_unix_path,
3435
};
3536

3637
/// [`IgnoreErrorKind`] describes the errors that can be returned by a Ignore action in Backends
@@ -59,6 +60,8 @@ pub enum IgnoreErrorKind {
5960
#[cfg(not(windows))]
6061
/// Error acquiring metadata for `{name}`: `{source:?}`
6162
AcquiringMetadataFailed { name: String, source: ignore::Error },
63+
/// Non-UTF8 filename is not allowed: `{0:?}`
64+
Utf8Error(#[from] std::str::Utf8Error),
6265
}
6366

6467
pub(crate) type IgnoreResult<T> = Result<T, IgnoreErrorKind>;
@@ -530,9 +533,7 @@ fn map_entry(
530533
let path = entry.into_path();
531534
let open = Some(OpenFile(path.clone()));
532535
let path = path.strip_prefix(base_path).unwrap();
533-
let path = WindowsPath::new(path.as_os_str().as_encoded_bytes())
534-
.to_path_buf()
535-
.with_encoding::<UnixEncoding>();
536+
let path = path_to_unix_path(path)?.to_path_buf();
536537
Ok(ReadSourceEntry { path, node, open })
537538
}
538539

@@ -731,7 +732,7 @@ fn map_entry(
731732
let path = entry.into_path();
732733
let open = Some(OpenFile(path.clone()));
733734
let path = path.strip_prefix(base_path).unwrap();
734-
let path = UnixPath::new(path.as_os_str().as_encoded_bytes()).to_path_buf();
735+
let path = path_to_unix_path(path)?.to_path_buf();
735736
Ok(ReadSourceEntry { path, node, open })
736737
}
737738

crates/core/src/backend/local_destination.rs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use crate::backend::node::NodeType;
3232
use crate::{
3333
backend::node::{ExtendedAttribute, Metadata, Node},
3434
error::{ErrorKind, RusticError, RusticResult},
35+
util::{typed_path_to_path, unix_path_to_path},
3536
};
3637

3738
/// [`LocalDestinationErrorKind`] describes the errors that can be returned by an action on the filesystem in Backends
@@ -103,9 +104,8 @@ pub enum LocalDestinationErrorKind {
103104
filename: PathBuf,
104105
source: std::io::Error,
105106
},
106-
#[cfg(windows)]
107107
/// Non-UTF8 filename is not allowed: `{0:?}`
108-
Utf8Error(std::str::Utf8Error),
108+
Utf8Error(#[from] std::str::Utf8Error),
109109
}
110110

111111
pub(crate) type LocalDestinationResult<T> = Result<T, LocalDestinationErrorKind>;
@@ -198,18 +198,8 @@ impl LocalDestination {
198198
if self.is_file {
199199
return Ok(self.path.clone());
200200
}
201-
#[cfg(not(windows))]
202-
{
203-
let item = PathBuf::from(item.as_ref());
204-
Ok(self.path.join(item))
205-
}
206-
#[cfg(windows)]
207-
{
208-
// only utf8 items are allowed on windows
209-
let item = std::str::from_utf8(item.as_ref().as_bytes())
210-
.map_err(LocalDestinationErrorKind::Utf8Error)?;
211-
Ok(self.path.join(item))
212-
}
201+
let item = unix_path_to_path(item.as_ref())?;
202+
Ok(self.path.join(item))
213203
}
214204

215205
/// Remove the given directory (relative to the base path)
@@ -645,11 +635,12 @@ impl LocalDestination {
645635

646636
match &node.node_type {
647637
NodeType::Symlink { .. } => {
648-
let linktarget: PathBuf =
649-
node.node_type.to_link().to_path_buf().try_into().unwrap(); // TODO: Error handling
650-
symlink(linktarget.clone(), &filename).map_err(|err| {
638+
let linktarget = node.node_type.to_link();
639+
let linktarget = typed_path_to_path(&linktarget)
640+
.map_err(LocalDestinationErrorKind::Utf8Error)?;
641+
symlink(&linktarget, &filename).map_err(|err| {
651642
LocalDestinationErrorKind::SymlinkingFailed {
652-
linktarget,
643+
linktarget: linktarget.to_path_buf(),
653644
filename,
654645
source: err,
655646
}

crates/core/src/util.rs

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// Utilities for handling paths on ``rustic_core``
2-
use std::borrow::Cow;
2+
///
3+
use std::{borrow::Cow, ffi::OsStr, path::Path, str::Utf8Error};
34

45
use globset::GlobMatcher;
56
use serde::{Serialize, Serializer};
@@ -49,7 +50,96 @@ where
4950
serializer.serialize_str(&s)
5051
}
5152

52-
/// Converts a [`TypedPath`] to an [`Cow<UnixPath>`].
53+
/// Converts a [`Path`] to a [`WindowsPath`].
54+
///
55+
/// # Arguments
56+
///
57+
/// * `path` - The path to convert.
58+
///
59+
/// # Errors
60+
///
61+
/// * If the path is non-unicode
62+
pub fn path_to_windows_path(path: &Path) -> Result<&WindowsPath, Utf8Error> {
63+
let str = std::str::from_utf8(path.as_os_str().as_encoded_bytes())?;
64+
Ok(WindowsPath::new(str))
65+
}
66+
67+
/// Converts a [`Path`] to a [`Cow<UnixPath>`].
68+
///
69+
/// Note: On windows, this converts prefixes into unix paths, e.g. "C:\dir" into "/c/dir"
70+
///
71+
/// # Arguments
72+
///
73+
/// * `path` - The path to convert.
74+
///
75+
/// # Errors
76+
///
77+
/// * If the path is non-unicode and we are using windows
78+
pub fn path_to_unix_path(path: &Path) -> Result<Cow<'_, UnixPath>, Utf8Error> {
79+
#[cfg(not(windows))]
80+
{
81+
let path = UnixPath::new(path.as_os_str().as_encoded_bytes());
82+
Ok(Cow::Borrowed(path))
83+
}
84+
#[cfg(windows)]
85+
{
86+
let path = windows_path_to_unix_path(path_to_windows_path(path)?);
87+
Ok(Cow::Owned(path))
88+
}
89+
}
90+
91+
/// Converts a [`TypedPath`] to a [`Cow<Path>`].
92+
///
93+
/// Note: On unix, this converts windows prefixes into unix paths, e.g. "C:\dir" into "/c/dir"
94+
///
95+
/// # Arguments
96+
///
97+
/// * `path` - The path to convert.
98+
///
99+
/// # Errors
100+
///
101+
/// * If the path is non-unicode and we are using windows
102+
pub fn typed_path_to_path<'a>(path: &'a TypedPath<'a>) -> Result<Cow<'a, Path>, Utf8Error> {
103+
#[cfg(not(windows))]
104+
{
105+
let path = match typed_path_to_unix_path(path) {
106+
Cow::Borrowed(path) => Cow::Borrowed(unix_path_to_path(path)?),
107+
Cow::Owned(path) => Cow::Owned(unix_path_to_path(&path)?.to_path_buf()),
108+
};
109+
Ok(path)
110+
}
111+
#[cfg(windows)]
112+
{
113+
// only utf8 items are allowed on windows
114+
let str = std::str::from_utf8(path.as_bytes())?;
115+
Ok(Cow::Borrowed(Path::new(str)))
116+
}
117+
}
118+
119+
/// Converts a [`UnixPath`] to a [`Path`].
120+
///
121+
/// # Arguments
122+
///
123+
/// * `path` - The path to convert.
124+
///
125+
/// # Errors
126+
///
127+
/// * If the path is non-unicode and we are using windows
128+
pub fn unix_path_to_path(path: &UnixPath) -> Result<&Path, Utf8Error> {
129+
#[cfg(not(windows))]
130+
{
131+
let osstr: &OsStr = path.as_ref();
132+
Ok(Path::new(osstr))
133+
}
134+
#[cfg(windows)]
135+
{
136+
// only utf8 items are allowed on windows
137+
let str = std::str::from_utf8(path.as_bytes())?;
138+
Ok(Path::new(str))
139+
}
140+
}
141+
142+
/// Converts a [`TypedPath`] to a [`Cow<UnixPath>`].
53143
///
54144
/// # Arguments
55145
///
@@ -64,6 +154,8 @@ pub fn typed_path_to_unix_path<'a>(path: &'a TypedPath<'_>) -> Cow<'a, UnixPath>
64154

65155
/// Converts a [`WindowsPath`] to a [`UnixPathBuf`].
66156
///
157+
/// Note: This converts windows prefixes into unix paths, e.g. "C:\dir" into "/c/dir"
158+
///
67159
/// # Arguments
68160
///
69161
/// * `path` - The path to convert.
@@ -77,13 +169,13 @@ pub fn windows_path_to_unix_path(path: &WindowsPath) -> UnixPathBuf {
77169
unix_path.push(UnixComponent::RootDir);
78170
match p.kind() {
79171
WindowsPrefix::Verbatim(p) | WindowsPrefix::DeviceNS(p) => {
80-
unix_path.push(p);
172+
unix_path.push(p.to_ascii_lowercase());
81173
}
82174
WindowsPrefix::VerbatimUNC(_, q) | WindowsPrefix::UNC(_, q) => {
83-
unix_path.push(q);
175+
unix_path.push(q.to_ascii_lowercase());
84176
}
85177
WindowsPrefix::VerbatimDisk(p) | WindowsPrefix::Disk(p) => {
86-
let c = vec![p];
178+
let c = vec![p.to_ascii_lowercase()];
87179
unix_path.push(&c);
88180
}
89181
}
@@ -120,11 +212,12 @@ mod tests {
120212
#[case(r#"\"#, "/")]
121213
#[case("/test/test2", "/test/test2")]
122214
#[case(r#"\test\test2"#, "/test/test2")]
123-
#[case(r#"C:\"#, "/C")]
124-
#[case(r#"C:\dir"#, "/C/dir")]
215+
#[case(r#"C:\"#, "/c")]
216+
#[case(r#"C:\dir"#, "/c/dir")]
217+
#[case(r#"d:\"#, "/d")]
125218
#[case(r#"a\b\"#, "a/b")]
126219
#[case(r#"a\b\c"#, "a/b/c")]
127-
fn test_typed_path_to_unix_path(#[case] windows_path: &str, #[case] unix_path: &str) {
220+
fn test_windows_path_to_unix_path(#[case] windows_path: &str, #[case] unix_path: &str) {
128221
assert_eq!(
129222
windows_path_to_unix_path(WindowsPath::new(windows_path))
130223
.to_str()

0 commit comments

Comments
 (0)