Skip to content

Commit 0e31d57

Browse files
author
Stephan Dilly
authored
New file tree (gitui-org#718)
1 parent ca35426 commit 0e31d57

20 files changed

+1936
-25
lines changed

Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ keywords = [
2121
[dependencies]
2222
scopetime = { path = "./scopetime", version = "0.1" }
2323
asyncgit = { path = "./asyncgit", version = "0.15" }
24+
filetree = { path = "./filetree" }
2425
crossterm = { version = "0.19", features = [ "serde" ] }
2526
clap = { version = "2.33", default-features = false }
2627
tui = { version = "0.15", default-features = false, features = ['crossterm', 'serde'] }

Makefile

+2-6
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,14 @@ fmt:
4545

4646
clippy:
4747
touch src/main.rs
48-
cargo clean -p gitui -p asyncgit -p scopetime
48+
cargo clean -p gitui -p asyncgit -p scopetime -p filetree
4949
cargo clippy --workspace --all-features
5050

5151
clippy-nightly:
5252
touch src/main.rs
53-
cargo clean -p gitui -p asyncgit -p scopetime
53+
cargo clean -p gitui -p asyncgit -p scopetime -p filetree
5454
cargo +nightly clippy --all-features
5555

56-
clippy-pedantic:
57-
cargo clean -p gitui -p asyncgit -p scopetime
58-
cargo clippy --all-features -- -W clippy::pedantic
59-
6056
check: fmt clippy test
6157

6258
install:

asyncgit/src/sync/status.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl From<StatusType> for StatusShow {
8888
}
8989
}
9090

91-
///
91+
/// gurantees sorting
9292
pub fn get_status(
9393
repo_path: &str,
9494
status_type: StatusType,

asyncgit/src/sync/tree.rs

+131-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use super::{utils::bytes2string, CommitId};
22
use crate::{error::Result, sync::utils::repo};
33
use git2::{Oid, Repository, Tree};
44
use scopetime::scope_time;
5-
use std::path::{Path, PathBuf};
5+
use std::{
6+
cmp::Ordering,
7+
path::{Path, PathBuf},
8+
};
69

710
/// `tree_files` returns a list of `FileTree`
811
#[derive(Debug, PartialEq)]
@@ -15,7 +18,7 @@ pub struct TreeFile {
1518
id: Oid,
1619
}
1720

18-
///
21+
/// guarantees sorting the result
1922
pub fn tree_files(
2023
repo_path: &str,
2124
commit: CommitId,
@@ -31,9 +34,40 @@ pub fn tree_files(
3134

3235
tree_recurse(&repo, &PathBuf::from("./"), &tree, &mut files)?;
3336

37+
sort_file_list(&mut files);
38+
3439
Ok(files)
3540
}
3641

42+
fn sort_file_list(files: &mut Vec<TreeFile>) {
43+
files.sort_by(|a, b| path_cmp(&a.path, &b.path));
44+
}
45+
46+
// applies topologically order on paths sorting
47+
fn path_cmp(a: &Path, b: &Path) -> Ordering {
48+
let mut comp_a = a.components().into_iter().peekable();
49+
let mut comp_b = b.components().into_iter().peekable();
50+
51+
loop {
52+
let a = comp_a.next();
53+
let b = comp_b.next();
54+
55+
let a_is_file = comp_a.peek().is_none();
56+
let b_is_file = comp_b.peek().is_none();
57+
58+
if a_is_file && !b_is_file {
59+
return Ordering::Greater;
60+
} else if !a_is_file && b_is_file {
61+
return Ordering::Less;
62+
}
63+
64+
let cmp = a.cmp(&b);
65+
if cmp != Ordering::Equal {
66+
return cmp;
67+
}
68+
}
69+
}
70+
3771
///
3872
pub fn tree_file_content(
3973
repo_path: &str,
@@ -109,4 +143,99 @@ mod tests {
109143
assert_eq!(files_c2.len(), 1);
110144
assert_ne!(files_c2[0], files[0]);
111145
}
146+
147+
#[test]
148+
fn test_sorting() {
149+
let mut list = vec!["file", "folder/file", "folder/afile"]
150+
.iter()
151+
.map(|f| TreeFile {
152+
path: PathBuf::from(f),
153+
filemode: 0,
154+
id: Oid::zero(),
155+
})
156+
.collect();
157+
158+
sort_file_list(&mut list);
159+
160+
assert_eq!(
161+
list.iter()
162+
.map(|f| f.path.to_string_lossy())
163+
.collect::<Vec<_>>(),
164+
vec![
165+
String::from("folder/afile"),
166+
String::from("folder/file"),
167+
String::from("file")
168+
]
169+
);
170+
}
171+
172+
#[test]
173+
fn test_sorting_folders() {
174+
let mut list = vec!["bfolder/file", "afolder/file"]
175+
.iter()
176+
.map(|f| TreeFile {
177+
path: PathBuf::from(f),
178+
filemode: 0,
179+
id: Oid::zero(),
180+
})
181+
.collect();
182+
183+
sort_file_list(&mut list);
184+
185+
assert_eq!(
186+
list.iter()
187+
.map(|f| f.path.to_string_lossy())
188+
.collect::<Vec<_>>(),
189+
vec![
190+
String::from("afolder/file"),
191+
String::from("bfolder/file"),
192+
]
193+
);
194+
}
195+
196+
#[test]
197+
fn test_sorting_folders2() {
198+
let mut list = vec!["bfolder/sub/file", "afolder/file"]
199+
.iter()
200+
.map(|f| TreeFile {
201+
path: PathBuf::from(f),
202+
filemode: 0,
203+
id: Oid::zero(),
204+
})
205+
.collect();
206+
207+
sort_file_list(&mut list);
208+
209+
assert_eq!(
210+
list.iter()
211+
.map(|f| f.path.to_string_lossy())
212+
.collect::<Vec<_>>(),
213+
vec![
214+
String::from("afolder/file"),
215+
String::from("bfolder/sub/file"),
216+
]
217+
);
218+
}
219+
220+
#[test]
221+
fn test_path_cmp() {
222+
assert_eq!(
223+
path_cmp(
224+
&PathBuf::from("bfolder/sub/file"),
225+
&PathBuf::from("afolder/file")
226+
),
227+
Ordering::Greater
228+
);
229+
}
230+
231+
#[test]
232+
fn test_path_file_cmp() {
233+
assert_eq!(
234+
path_cmp(
235+
&PathBuf::from("a"),
236+
&PathBuf::from("afolder/file")
237+
),
238+
Ordering::Greater
239+
);
240+
}
112241
}

filetree/Cargo.toml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "filetree"
3+
version = "0.1.0"
4+
authors = ["Stephan Dilly <[email protected]>"]
5+
edition = "2018"
6+
description = "filetree abstraction based on a sorted path list"
7+
homepage = "https://github.com/extrawurst/gitui"
8+
repository = "https://github.com/extrawurst/gitui"
9+
readme = "README.md"
10+
license-file = "LICENSE.md"
11+
categories = ["command-line-utilities"]
12+
keywords = ["gui","cli","terminal","ui"]
13+
14+
[dependencies]
15+
thiserror = "1.0"
16+
17+
[dev-dependencies]
18+
pretty_assertions = "0.7"

filetree/LICENSE.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE.md

filetree/src/error.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use std::{num::TryFromIntError, path::PathBuf};
2+
use thiserror::Error;
3+
4+
#[derive(Error, Debug)]
5+
pub enum Error {
6+
#[error("InvalidPath: `{0}`")]
7+
InvalidPath(PathBuf),
8+
9+
#[error("InvalidFilePath: `{0}`")]
10+
InvalidFilePath(String),
11+
12+
#[error("TryFromInt error:{0}")]
13+
IntConversion(#[from] TryFromIntError),
14+
}
15+
16+
pub type Result<T> = std::result::Result<T, Error>;

0 commit comments

Comments
 (0)