1use crate::SymlinkCheck;
2use bstr::BStr;
3use gix_fs::stack::ToNormalPathComponents;
4use gix_fs::Stack;
5use std::borrow::Cow;
6use std::path::{Path, PathBuf};
7
8impl SymlinkCheck {
9 pub fn new(root: PathBuf) -> Self {
11 Self {
12 inner: gix_fs::Stack::new(root),
13 }
14 }
15
16 pub fn verified_path(&mut self, relative_path: impl ToNormalPathComponents) -> std::io::Result<&Path> {
28 self.inner.make_relative_path_current(relative_path, &mut Delegate)?;
29 Ok(self.inner.current())
30 }
31
32 pub fn verified_path_allow_nonexisting(&mut self, relative_path: &BStr) -> std::io::Result<Cow<'_, Path>> {
36 let rela_path = gix_path::try_from_bstr(relative_path).map_err(std::io::Error::other)?;
37 if let Err(err) = self.verified_path(rela_path.as_ref()) {
38 if err.kind() == std::io::ErrorKind::NotFound {
39 Ok(Cow::Owned(self.inner.root().join(rela_path)))
40 } else {
41 Err(err)
42 }
43 } else {
44 Ok(Cow::Borrowed(self.inner.current()))
45 }
46 }
47}
48
49struct Delegate;
50
51impl gix_fs::stack::Delegate for Delegate {
52 fn push_directory(&mut self, _stack: &Stack) -> std::io::Result<()> {
53 Ok(())
54 }
55
56 #[cfg_attr(windows, allow(unused_variables))]
57 fn push(&mut self, is_last_component: bool, stack: &Stack) -> std::io::Result<()> {
58 #[cfg(windows)]
59 {
60 Ok(())
61 }
62 #[cfg(not(windows))]
63 {
64 if is_last_component {
65 return Ok(());
66 }
67
68 if stack.current().symlink_metadata()?.is_symlink() {
69 return Err(std::io::Error::new(
70 std::io::ErrorKind::Other,
71 "Cannot step through symlink to perform an lstat",
72 ));
73 }
74 Ok(())
75 }
76 }
77
78 fn pop_directory(&mut self) {}
79}