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