Skip to content

Commit af9ca15

Browse files
committed
feat!: support to obtain Attributes using the Cache type.
1 parent 1d15173 commit af9ca15

File tree

13 files changed

+646
-180
lines changed

13 files changed

+646
-180
lines changed

gix-worktree/src/cache/delegate.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use crate::cache::State;
2+
use crate::PathIdMapping;
3+
4+
pub struct StackDelegate<'a, Find> {
5+
pub state: &'a mut State,
6+
pub buf: &'a mut Vec<u8>,
7+
pub is_dir: bool,
8+
pub id_mappings: &'a Vec<PathIdMapping>,
9+
pub find: Find,
10+
}
11+
12+
impl<'a, Find, E> gix_fs::stack::Delegate for StackDelegate<'a, Find>
13+
where
14+
Find: for<'b> FnMut(&gix_hash::oid, &'b mut Vec<u8>) -> Result<gix_object::BlobRef<'b>, E>,
15+
E: std::error::Error + Send + Sync + 'static,
16+
{
17+
fn push_directory(&mut self, stack: &gix_fs::Stack) -> std::io::Result<()> {
18+
match &mut self.state {
19+
State::CreateDirectoryAndAttributesStack { attributes, .. } => {
20+
attributes.push_directory(
21+
stack.root(),
22+
stack.current(),
23+
self.buf,
24+
self.id_mappings,
25+
&mut self.find,
26+
)?;
27+
}
28+
State::AttributesAndIgnoreStack { ignore, attributes } => {
29+
attributes.push_directory(
30+
stack.root(),
31+
stack.current(),
32+
self.buf,
33+
self.id_mappings,
34+
&mut self.find,
35+
)?;
36+
ignore.push_directory(
37+
stack.root(),
38+
stack.current(),
39+
self.buf,
40+
self.id_mappings,
41+
&mut self.find,
42+
)?
43+
}
44+
State::IgnoreStack(ignore) => ignore.push_directory(
45+
stack.root(),
46+
stack.current(),
47+
self.buf,
48+
self.id_mappings,
49+
&mut self.find,
50+
)?,
51+
}
52+
Ok(())
53+
}
54+
55+
fn push(&mut self, is_last_component: bool, stack: &gix_fs::Stack) -> std::io::Result<()> {
56+
match &mut self.state {
57+
State::CreateDirectoryAndAttributesStack {
58+
#[cfg(debug_assertions)]
59+
test_mkdir_calls,
60+
unlink_on_collision,
61+
attributes: _,
62+
} => {
63+
#[cfg(debug_assertions)]
64+
{
65+
create_leading_directory(
66+
is_last_component,
67+
stack,
68+
self.is_dir,
69+
test_mkdir_calls,
70+
*unlink_on_collision,
71+
)?
72+
}
73+
#[cfg(not(debug_assertions))]
74+
{
75+
create_leading_directory(is_last_component, stack, self.is_dir, *unlink_on_collision)?
76+
}
77+
}
78+
State::AttributesAndIgnoreStack { .. } | State::IgnoreStack(_) => {}
79+
}
80+
Ok(())
81+
}
82+
83+
fn pop_directory(&mut self) {
84+
match &mut self.state {
85+
State::CreateDirectoryAndAttributesStack { attributes, .. } => {
86+
attributes.pop_directory();
87+
}
88+
State::AttributesAndIgnoreStack { attributes, ignore } => {
89+
attributes.pop_directory();
90+
ignore.pop_directory();
91+
}
92+
State::IgnoreStack(ignore) => {
93+
ignore.pop_directory();
94+
}
95+
}
96+
}
97+
}
98+
99+
fn create_leading_directory(
100+
is_last_component: bool,
101+
stack: &gix_fs::Stack,
102+
is_dir: bool,
103+
#[cfg(debug_assertions)] mkdir_calls: &mut usize,
104+
unlink_on_collision: bool,
105+
) -> std::io::Result<()> {
106+
if is_last_component && !is_dir {
107+
return Ok(());
108+
}
109+
#[cfg(debug_assertions)]
110+
{
111+
*mkdir_calls += 1;
112+
}
113+
match std::fs::create_dir(stack.current()) {
114+
Ok(()) => Ok(()),
115+
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
116+
let meta = stack.current().symlink_metadata()?;
117+
if meta.is_dir() {
118+
Ok(())
119+
} else if unlink_on_collision {
120+
if meta.file_type().is_symlink() {
121+
gix_fs::symlink::remove(stack.current())?;
122+
} else {
123+
std::fs::remove_file(stack.current())?;
124+
}
125+
#[cfg(debug_assertions)]
126+
{
127+
*mkdir_calls += 1;
128+
}
129+
std::fs::create_dir(stack.current())
130+
} else {
131+
Err(err)
132+
}
133+
}
134+
Err(err) => Err(err),
135+
}
136+
}

gix-worktree/src/cache/mod.rs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use bstr::{BStr, ByteSlice};
55
use gix_hash::oid;
66

77
use super::Cache;
8-
use crate::PathOidMapping;
8+
use crate::PathIdMapping;
99

1010
#[derive(Clone)]
1111
pub enum State {
@@ -32,6 +32,7 @@ pub enum State {
3232
}
3333

3434
#[cfg(debug_assertions)]
35+
/// debug builds only for use in tests.
3536
impl Cache {
3637
pub fn set_case(&mut self, case: gix_glob::pattern::Case) {
3738
self.case = case;
@@ -65,32 +66,40 @@ pub struct Platform<'a> {
6566
is_dir: Option<bool>,
6667
}
6768

69+
/// Initialization
6870
impl Cache {
69-
/// Create a new instance with `worktree_root` being the base for all future paths we handle, assuming it to be valid which includes
70-
/// symbolic links to be included in it as well.
71-
/// The `case` configures attribute and exclusion query case sensitivity.
71+
/// Create a new instance with `worktree_root` being the base for all future paths we match.
72+
/// `state` defines the capabilities of the cache.
73+
/// The `case` configures attribute and exclusion case sensitivity at *query time*, which should match the case that
74+
/// `state` might be configured with.
75+
/// `buf` is used when reading files, and `id_mappings` should have been created with [State::id_mappings_from_index()].
7276
pub fn new(
7377
worktree_root: impl Into<PathBuf>,
7478
state: State,
7579
case: gix_glob::pattern::Case,
7680
buf: Vec<u8>,
77-
attribute_files_in_index: Vec<PathOidMapping>,
81+
id_mappings: Vec<PathIdMapping>,
7882
) -> Self {
7983
let root = worktree_root.into();
8084
Cache {
8185
stack: gix_fs::Stack::new(root),
8286
state,
8387
case,
8488
buf,
85-
attribute_files_in_index,
89+
id_mappings,
8690
}
8791
}
92+
}
8893

89-
/// Append the `relative` path to the root directory the cache contains and efficiently create leading directories
90-
/// unless `is_dir` is known (`Some(…)`) then `relative` points to a directory itself in which case the entire resulting
94+
/// Mutable Access
95+
impl Cache {
96+
/// Append the `relative` path to the root directory of the cache and efficiently create leading directories, while assuring that no
97+
/// symlinks are in that path.
98+
/// Unless `is_dir` is known with `Some(…)`, then `relative` points to a directory itself in which case the entire resulting
9199
/// path is created as directory. If it's not known it is assumed to be a file.
100+
/// `find` maybe used to lookup objects from an [id mapping][crate::cache::State::id_mappings_from_index()], with mappnigs
92101
///
93-
/// Provide access to cached information for that `relative` entry via the platform returned.
102+
/// Provide access to cached information for that `relative` path via the returned platform.
94103
pub fn at_path<Find, E>(
95104
&mut self,
96105
relative: impl AsRef<Path>,
@@ -101,19 +110,27 @@ impl Cache {
101110
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E>,
102111
E: std::error::Error + Send + Sync + 'static,
103112
{
104-
let mut delegate = platform::StackDelegate {
113+
let mut delegate = StackDelegate {
105114
state: &mut self.state,
106115
buf: &mut self.buf,
107116
is_dir: is_dir.unwrap_or(false),
108-
attribute_files_in_index: &self.attribute_files_in_index,
117+
id_mappings: &self.id_mappings,
109118
find,
110119
};
111120
self.stack.make_relative_path_current(relative, &mut delegate)?;
112121
Ok(Platform { parent: self, is_dir })
113122
}
114123

115-
/// **Panics** on illformed UTF8 in `relative`
116-
// TODO: more docs
124+
/// Obtain a platform for lookups from a repo-`relative` path, typically obtained from an index entry. `is_dir` should reflect
125+
/// whether it's a directory or not, or left at `None` if unknown.
126+
/// `find` maybe used to lookup objects from an [id mapping][crate::cache::State::id_mappings_from_index()].
127+
/// All effects are similar to [`at_path()`][Self::at_path()].
128+
///
129+
/// If `relative` ends with `/` and `is_dir` is `None`, it is automatically assumed to be a directory.
130+
///
131+
/// ### Panics
132+
///
133+
/// on illformed UTF8 in `relative`
117134
pub fn at_entry<'r, Find, E>(
118135
&mut self,
119136
relative: impl Into<&'r BStr>,
@@ -130,10 +147,17 @@ impl Cache {
130147
self.at_path(
131148
relative_path,
132149
is_dir.or_else(|| relative.ends_with_str("/").then_some(true)),
133-
// is_dir,
134150
find,
135151
)
136152
}
153+
}
154+
155+
/// Access
156+
impl Cache {
157+
/// Return the state for introspection.
158+
pub fn state(&self) -> &State {
159+
&self.state
160+
}
137161

138162
/// Return the base path against which all entries or paths should be relative to when querying.
139163
///
@@ -143,6 +167,9 @@ impl Cache {
143167
}
144168
}
145169

170+
mod delegate;
171+
use delegate::StackDelegate;
172+
146173
mod platform;
147174
///
148175
pub mod state;

0 commit comments

Comments
 (0)