summaryrefslogtreecommitdiff
path: root/src/cmd/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/util')
-rw-r--r--src/cmd/util/args.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/cmd/util/args.rs b/src/cmd/util/args.rs
new file mode 100644
index 0000000..e372c82
--- /dev/null
+++ b/src/cmd/util/args.rs
@@ -0,0 +1,139 @@
+// Copyright © 2022 Kim Altintop <kim@eagain.io>
+// SPDX-License-Identifier: GPL-2.0-only WITH openvpn-openssl-exception
+
+use core::{
+ fmt,
+ slice,
+ str::FromStr,
+};
+use std::{
+ borrow::Borrow,
+ convert::Infallible,
+ env,
+ path::PathBuf,
+ vec,
+};
+
+pub use crate::git::Refname;
+use crate::{
+ cfg::paths,
+ git,
+};
+
+/// Search path akin to the `PATH` environment variable.
+#[derive(Clone, Debug)]
+pub struct SearchPath(Vec<PathBuf>);
+
+impl SearchPath {
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
+impl fmt::Display for SearchPath {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ std::env::join_paths(&self.0)
+ .unwrap()
+ .to_string_lossy()
+ .fmt(f)
+ }
+}
+
+impl FromStr for SearchPath {
+ type Err = Infallible;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(Self(env::split_paths(s).collect()))
+ }
+}
+
+impl IntoIterator for SearchPath {
+ type Item = PathBuf;
+ type IntoIter = vec::IntoIter<PathBuf>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.into_iter()
+ }
+}
+
+impl<'a> IntoIterator for &'a SearchPath {
+ type Item = &'a PathBuf;
+ type IntoIter = slice::Iter<'a, PathBuf>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.iter()
+ }
+}
+
+/// A [`SearchPath`] with a [`Default`] appropriate for `it` identity
+/// repositories.
+#[derive(Clone, Debug)]
+pub struct IdSearchPath(SearchPath);
+
+impl IdSearchPath {
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ /// Attempt to open each path element as a git repository
+ ///
+ /// The repositories will be opened as bare, even if they aren't. No error
+ /// is returned if a repo could not be opened (e.g. because it is not a git
+ /// repository).
+ pub fn open_git(&self) -> Vec<git2::Repository> {
+ let mut rs = Vec::with_capacity(self.len());
+ for path in self {
+ if let Ok(repo) = git::repo::open_bare(path) {
+ rs.push(repo);
+ }
+ }
+
+ rs
+ }
+}
+
+impl Default for IdSearchPath {
+ fn default() -> Self {
+ Self(SearchPath(vec![paths::ids()]))
+ }
+}
+
+impl fmt::Display for IdSearchPath {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl FromStr for IdSearchPath {
+ type Err = <SearchPath as FromStr>::Err;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ s.parse().map(Self)
+ }
+}
+
+impl IntoIterator for IdSearchPath {
+ type Item = <SearchPath as IntoIterator>::Item;
+ type IntoIter = <SearchPath as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.into_iter()
+ }
+}
+
+impl<'a> IntoIterator for &'a IdSearchPath {
+ type Item = <&'a SearchPath as IntoIterator>::Item;
+ type IntoIter = <&'a SearchPath as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.borrow().into_iter()
+ }
+}