summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVegard Skui <me@vegardskui.com>2023-04-02 16:37:27 +0200
committerGitHub <noreply@github.com>2023-04-02 16:37:27 +0200
commit4bca74eca29e159f0d6f27db432927012848408c (patch)
tree185437236118c02e1829cc63e14ef5a27005e264
parentaef799bfb089c5d259354208a6bcd5a0b639888f (diff)
feat(fossil): detection of Fossil check-outs in subdirectories (#4910)
* Move PathExt::device_id() outside modules module * Add upwards_sibling_scan-function * Fix Fossil check-out detection in subdirectories * Use shared upwards scanning function in hg_branch * Let the caller specify if they're looking for a file or a folder * fix merge --------- Co-authored-by: David Knaack <davidkna@users.noreply.github.com>
-rw-r--r--src/context.rs54
-rw-r--r--src/modules/fossil_branch.rs24
-rw-r--r--src/modules/hg_branch.rs19
-rw-r--r--src/modules/utils/path.rs25
-rw-r--r--src/test/mod.rs1
-rw-r--r--src/utils.rs34
6 files changed, 106 insertions, 51 deletions
diff --git a/src/context.rs b/src/context.rs
index 0994a8e55..b0bfc0fd5 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -1,7 +1,7 @@
use crate::config::{ModuleConfig, StarshipConfig};
use crate::configs::StarshipRootConfig;
use crate::module::Module;
-use crate::utils::{create_command, exec_timeout, read_file, CommandOutput};
+use crate::utils::{create_command, exec_timeout, read_file, CommandOutput, PathExt};
use crate::modules;
use crate::utils::{self, home_dir};
@@ -248,6 +248,16 @@ impl<'a> Context<'a> {
})
}
+ /// Begins an ancestor scan at the current directory, see [`ScanAncestors`] for available
+ /// methods.
+ pub fn begin_ancestor_scan(&'a self) -> ScanAncestors<'a> {
+ ScanAncestors {
+ path: &self.current_dir,
+ files: &[],
+ folders: &[],
+ }
+ }
+
/// Will lazily get repo root and branch when a module requests it.
pub fn get_repo(&self) -> Result<&Repo, Box<gix::discover::Error>> {
self.repo
@@ -607,6 +617,48 @@ impl<'a> ScanDir<'a> {
}
}
+/// Scans the ancestors of a given path until a directory containing one of the given files or
+/// folders is found.
+pub struct ScanAncestors<'a> {
+ path: &'a Path,
+ files: &'a [&'a str],
+ folders: &'a [&'a str],
+}
+
+impl<'a> ScanAncestors<'a> {
+ #[must_use]
+ pub const fn set_files(mut self, files: &'a [&'a str]) -> Self {
+ self.files = files;
+ self
+ }
+
+ #[must_use]
+ pub const fn set_folders(mut self, folders: &'a [&'a str]) -> Self {
+ self.folders = folders;
+ self
+ }
+
+ /// Scans upwards starting from the initial path until a directory containing one of the given
+ /// files or folders is found.
+ ///
+ /// The scan does not cross device boundaries.
+ pub fn scan(&self) -> Option<&'a Path> {
+ let initial_device_id = self.path.device_id();
+ for dir in self.path.ancestors() {
+ if initial_device_id != dir.device_id() {
+ break;
+ }
+
+ if self.files.iter().any(|name| dir.join(name).is_file())
+ || self.folders.iter().any(|name| dir.join(name).is_dir())
+ {
+ return Some(dir);
+ }
+ }
+ None
+ }
+}
+
fn get_current_branch(repository: &Repository) -> Option<String> {
let name = repository.head_name().ok()??;
let shorthand = name.shorten();
diff --git a/src/modules/fossil_branch.rs b/src/modules/fossil_branch.rs
index da4dabbdb..7cc4d331b 100644
--- a/src/modules/fossil_branch.rs
+++ b/src/modules/fossil_branch.rs
@@ -22,15 +22,11 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
} else {
".fslckout"
};
-
- let is_checkout = context
- .try_begin_scan()?
+ // See if we're in a check-out by scanning upwards for a directory containing the checkout_db file
+ context
+ .begin_ancestor_scan()
.set_files(&[checkout_db])
- .is_match();
-
- if !is_checkout {
- return None;
- }
+ .scan()?;
let len = if config.truncation_length <= 0 {
log::warn!(
@@ -144,6 +140,18 @@ mod tests {
}
#[test]
+ fn test_fossil_branch_subdir() -> io::Result<()> {
+ let tempdir = fixture_repo(FixtureProvider::Fossil)?;
+ let checkout_dir = tempdir.path();
+ expect_fossil_branch_with_config(
+ &checkout_dir.join("subdir"),
+ None,
+ &[Expect::BranchName("topic-branch"), Expect::NoTruncation],
+ );
+ tempdir.close()
+ }
+
+ #[test]
fn test_fossil_branch_configured() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
let checkout_dir = tempdir.path();
diff --git a/src/modules/hg_branch.rs b/src/modules/hg_branch.rs
index 8a9f84ae2..c4c4a2a30 100644
--- a/src/modules/hg_branch.rs
+++ b/src/modules/hg_branch.rs
@@ -1,4 +1,4 @@
-use std::io::{Error, ErrorKind};
+use std::io::Error;
use std::path::Path;
use super::utils::truncate::truncate_text;
@@ -6,7 +6,6 @@ use super::{Context, Module, ModuleConfig};
use crate::configs::hg_branch::HgBranchConfig;
use crate::formatter::StringFormatter;
-use crate::modules::utils::path::PathExt;
use crate::utils::read_file;
/// Creates a module with the Hg bookmark or branch in the current directory
@@ -32,7 +31,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
config.truncation_length as usize
};
- let repo_root = get_hg_repo_root(context).ok()?;
+ let repo_root = context.begin_ancestor_scan().set_folders(&[".hg"]).scan()?;
let branch_name = get_hg_current_bookmark(repo_root).unwrap_or_else(|_| {
get_hg_branch_name(repo_root).unwrap_or_else(|_| String::from("default"))
});
@@ -73,20 +72,6 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module)
}
-fn get_hg_repo_root<'a>(ctx: &'a Context) -> Result<&'a Path, Error> {
- let dir = ctx.current_dir.as_path();
- let dev_id = dir.device_id();
- for root_dir in dir.ancestors() {
- if dev_id != root_dir.device_id() {
- break;
- }
- if root_dir.join(".hg").is_dir() {
- return Ok(root_dir);
- }
- }
- Err(Error::new(ErrorKind::Other, "No .hg found!"))
-}
-
fn get_hg_branch_name(hg_root: &Path) -> Result<String, Error> {
match read_file(hg_root.join(".hg").join("branch")) {
Ok(b) => Ok(b.trim().to_string()),
diff --git a/src/modules/utils/path.rs b/src/modules/utils/path.rs
index 5f5ad4847..2bbb02b51 100644
--- a/src/modules/utils/path.rs
+++ b/src/modules/utils/path.rs
@@ -13,8 +13,6 @@ pub trait PathExt {
/// E.g. `\\?\UNC\server\share\foo` => `\foo`
/// E.g. `/foo/bar` => `/foo/bar`
fn without_prefix(&self) -> &Path;
- /// Get device / volume info
- fn device_id(&self) -> Option<u64>;
}
#[cfg(windows)]
@@ -82,11 +80,6 @@ impl PathExt for Path {
let (_, path) = normalize::normalize_path(self);
path
}
-
- fn device_id(&self) -> Option<u64> {
- // Maybe it should use unimplemented!
- Some(42u64)
- }
}
// NOTE: Windows path prefixes are only parsed on Windows.
@@ -107,24 +100,6 @@ impl PathExt for Path {
fn without_prefix(&self) -> &Path {
self
}
-
- #[cfg(target_os = "linux")]
- fn device_id(&self) -> Option<u64> {
- use std::os::linux::fs::MetadataExt;
- match self.metadata() {
- Ok(m) => Some(m.st_dev()),
- Err(_) => None,
- }
- }
-
- #[cfg(all(unix, not(target_os = "linux")))]
- fn device_id(&self) -> Option<u64> {
- use std::os::unix::fs::MetadataExt;
- match self.metadata() {
- Ok(m) => Some(m.dev()),
- Err(_) => None,
- }
- }
}
#[cfg(test)]
diff --git a/src/test/mod.rs b/src/test/mod.rs
index 48bb51ad3..23b0d6fb3 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -182,6 +182,7 @@ pub fn fixture_repo(provider: FixtureProvider) -> io::Result<TempDir> {
".fslckout"
};
let path = tempfile::tempdir()?;
+ fs::create_dir(path.path().join("subdir"))?;
fs::OpenOptions::new()
.create(true)
.write(true)
diff --git a/src/utils.rs b/src/utils.rs
index 54b9e7393..aad9f297a 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -644,6 +644,40 @@ pub fn encode_to_hex(slice: &[u8]) -> String {
String::from_utf8(dst).unwrap()
}
+pub trait PathExt {
+ /// Get device / volume info
+ fn device_id(&self) -> Option<u64>;
+}
+
+#[cfg(windows)]
+impl PathExt for Path {
+ fn device_id(&self) -> Option<u64> {
+ // Maybe it should use unimplemented!
+ Some(42u64)
+ }
+}
+
+#[cfg(not(windows))]
+impl PathExt for Path {
+ #[cfg(target_os = "linux")]
+ fn device_id(&self) -> Option<u64> {
+ use std::os::linux::fs::MetadataExt;
+ match self.metadata() {
+ Ok(m) => Some(m.st_dev()),
+ Err(_) => None,
+ }
+ }
+
+ #[cfg(all(unix, not(target_os = "linux")))]
+ fn device_id(&self) -> Option<u64> {
+ use std::os::unix::fs::MetadataExt;
+ match self.metadata() {
+ Ok(m) => Some(m.dev()),
+ Err(_) => None,
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;