Skip to content

Commit

Permalink
Make justfile_path optional in search::Search
Browse files Browse the repository at this point in the history
Change justfile_path from PathBuf to Option<PathBuf> which
will enable loading justfile from stream like STDIN.
  • Loading branch information
Marcin Drzymala committed Aug 16, 2024
1 parent 16164a4 commit fb28b25
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 87 deletions.
45 changes: 12 additions & 33 deletions src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,34 +392,21 @@ fn just_pid(_context: Context) -> FunctionResult {
}

fn justfile(context: Context) -> FunctionResult {
context
.evaluator
.context
.search
.justfile_path
.to_str()
.map(str::to_owned)
.ok_or_else(|| {
format!(
"Justfile path is not valid unicode: {}",
context.evaluator.context.search.justfile_path.display()
)
})
match context.evaluator.context.search.justfile_path.as_ref() {
None => Err(String::from("Justfile is not a file on disk")),
Some(path) => match path.to_str() {
None => Err(format!("Justfile path is not valid unicode: {}", path.display())),
Some(path) => Ok(String::from(path)),
},
}
}

fn justfile_directory(context: Context) -> FunctionResult {
let justfile_directory = context
.evaluator
.context
.search
.justfile_path
.parent()
.ok_or_else(|| {
format!(
"Could not resolve justfile directory. Justfile `{}` had no parent.",
context.evaluator.context.search.justfile_path.display()
)
})?;
.justfile_directory();

justfile_directory
.to_str()
Expand Down Expand Up @@ -449,9 +436,7 @@ fn module_directory(context: Context) -> FunctionResult {
.evaluator
.context
.search
.justfile_path
.parent()
.unwrap()
.justfile_directory()
.join(&context.evaluator.context.module.source)
.parent()
.unwrap()
Expand All @@ -477,9 +462,7 @@ fn module_file(context: Context) -> FunctionResult {
.evaluator
.context
.search
.justfile_path
.parent()
.unwrap()
.justfile_directory()
.join(&context.evaluator.context.module.source)
.to_str()
.map(str::to_owned)
Expand Down Expand Up @@ -588,9 +571,7 @@ fn source_directory(context: Context) -> FunctionResult {
.evaluator
.context
.search
.justfile_path
.parent()
.unwrap()
.justfile_directory()
.join(context.name.token.path)
.parent()
.unwrap()
Expand All @@ -609,9 +590,7 @@ fn source_file(context: Context) -> FunctionResult {
.evaluator
.context
.search
.justfile_path
.parent()
.unwrap()
.justfile_directory()
.join(context.name.token.path)
.to_str()
.map(str::to_owned)
Expand Down
129 changes: 94 additions & 35 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"

#[derive(Debug)]
pub(crate) struct Search {
pub(crate) justfile_path: PathBuf,
pub(crate) justfile_path: Option<PathBuf>,
pub(crate) working_directory: PathBuf,
}

Expand Down Expand Up @@ -42,44 +42,42 @@ impl Search {
SearchConfig::FromInvocationDirectory => Self::find_next(invocation_directory),
SearchConfig::FromSearchDirectory { search_directory } => {
let search_directory = Self::clean(invocation_directory, search_directory);
let justfile = Self::justfile(&search_directory)?;
let working_directory = Self::working_directory_from_justfile(&justfile)?;
Ok(Self {
justfile_path: justfile,
working_directory,
justfile_path: Self::justfile(&search_directory)?,
working_directory: search_directory,
})
}
SearchConfig::GlobalJustfile => Ok(Self {
justfile_path: Self::global_justfile_paths()
.iter()
.find(|path| path.exists())
.cloned()
.ok_or(SearchError::GlobalJustfileNotFound)?,
.cloned(),
working_directory: Self::project_root(invocation_directory)?,
}),
SearchConfig::WithJustfile { justfile } => {
let justfile = Self::clean(invocation_directory, justfile);
let working_directory = Self::working_directory_from_justfile(&justfile)?;
Ok(Self {
justfile_path: justfile,
working_directory,
justfile_path: Some(Self::clean(invocation_directory, justfile)),
working_directory: invocation_directory.into(),
})
}
SearchConfig::WithJustfileAndWorkingDirectory {
justfile,
working_directory,
} => Ok(Self {
justfile_path: Self::clean(invocation_directory, justfile),
justfile_path: Some(Self::clean(invocation_directory, justfile)),
working_directory: Self::clean(invocation_directory, working_directory),
}),
}
}

pub(crate) fn find_next(starting_dir: &Path) -> SearchResult<Self> {
let justfile = Self::justfile(starting_dir)?;
let working_directory = Self::working_directory_from_justfile(&justfile)?;
let justfile_path = Self::justfile(starting_dir)?;
let working_directory = match &justfile_path {
Some(path) => path.parent().unwrap(),
None => starting_dir,
}.into();
Ok(Self {
justfile_path: justfile,
justfile_path,
working_directory,
})
}
Expand All @@ -93,7 +91,7 @@ impl Search {
let working_directory = Self::project_root(invocation_directory)?;
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
Ok(Self {
justfile_path: justfile,
justfile_path: Some(justfile),
working_directory,
})
}
Expand All @@ -102,30 +100,28 @@ impl Search {
let working_directory = Self::project_root(&search_directory)?;
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
Ok(Self {
justfile_path: justfile,
justfile_path: Some(justfile),
working_directory,
})
}
SearchConfig::GlobalJustfile => Err(SearchError::GlobalJustfileInit),
SearchConfig::WithJustfile { justfile } => {
let justfile = Self::clean(invocation_directory, justfile);
let working_directory = Self::working_directory_from_justfile(&justfile)?;
Ok(Self {
justfile_path: justfile,
working_directory,
justfile_path: Some(Self::clean(invocation_directory, justfile)),
working_directory: invocation_directory.into(),
})
}
SearchConfig::WithJustfileAndWorkingDirectory {
justfile,
working_directory,
} => Ok(Self {
justfile_path: Self::clean(invocation_directory, justfile),
justfile_path: Some(Self::clean(invocation_directory, justfile)),
working_directory: Self::clean(invocation_directory, working_directory),
}),
}
}

pub(crate) fn justfile(directory: &Path) -> SearchResult<PathBuf> {
pub(crate) fn justfile(directory: &Path) -> SearchResult<Option<PathBuf>> {
for directory in directory.ancestors() {
let mut candidates = BTreeSet::new();

Expand All @@ -149,7 +145,7 @@ impl Search {

match candidates.len() {
0 => {}
1 => return Ok(candidates.into_iter().next().unwrap()),
1 => return Ok(candidates.pop_first()),
_ => return Err(SearchError::MultipleCandidates { candidates }),
}
}
Expand Down Expand Up @@ -198,18 +194,81 @@ impl Search {
Ok(directory.to_owned())
}

fn working_directory_from_justfile(justfile: &Path) -> SearchResult<PathBuf> {
Ok(
justfile
.parent()
.ok_or_else(|| SearchError::JustfileHadNoParent {
path: justfile.to_path_buf(),
})?
.to_owned(),
)
/// Returns the path to the directory where the jusfile is located.
/// Returns an empty path if the justfile path is relative and has only one component.
/// Returns an empty path if the justfile path is itself empty.
/// Returns the current working directory if the justfile is not a file on disk.
pub fn justfile_directory(&self) -> PathBuf {
if let Some(ref path) = self.justfile_path {
return match path.parent() {
Some(path) => PathBuf::from(path),
None => PathBuf::from(""),
}
}
self.working_directory.clone()
}

/// Returns the final component of the justfile path or the default justfile name.
pub fn justfile_name(&self) -> OsString {
if let Some(ref path) = self.justfile_path {
if let Some(file_name) = path.file_name() {
return file_name.to_owned();
}
}
DEFAULT_JUSTFILE_NAME.into()
}

/// Returns the path to the justfile including the justfile name.
/// Returns a default justfile name in the current working dir if the path is empty.
pub fn justfile_path(&self) -> PathBuf {
if let Some(ref path) = self.justfile_path {
return path.into();
}
self.justfile_directory().join(DEFAULT_JUSTFILE_NAME)
}
}

#[cfg(test)]
mod justfile_directory_tests {
use super::*;

#[test]
fn should_return_working_dir_when_justfile_path_is_empty() {
let search = Search {
justfile_path: None,
working_directory: PathBuf::from("/work/ing/dir"),
};
assert_eq!(search.justfile_directory(), PathBuf::from("/work/ing/dir"));
}

#[test]
fn should_return_justfile_parent_if_the_same_as_working_dir() {
let search = Search {
justfile_path: Some(PathBuf::from("/work/ing/dir/justfile")),
working_directory: PathBuf::from("/work/ing/dir"),
};
assert_eq!(search.justfile_directory(), PathBuf::from("/work/ing/dir"));
}

#[test]
fn should_return_justfile_parent_if_inside_of_working_dir() {
let search = Search {
justfile_path: Some(PathBuf::from("/work/ing/dir/subdir/justfile")),
working_directory: PathBuf::from("/work/ing/dir"),
};
assert_eq!(search.justfile_directory(), PathBuf::from("/work/ing/dir/subdir"));
}

#[test]
fn should_return_justfile_parent_if_outside_of_working_dir() {
let search = Search {
justfile_path: Some(PathBuf::from("/other/dir/justfile")),
working_directory: PathBuf::from("/work/ing/dir"),
};
assert_eq!(search.justfile_directory(), PathBuf::from("/other/dir"));
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -313,7 +372,7 @@ mod tests {
Ok(found_path) => {
path.pop();
path.push(DEFAULT_JUSTFILE_NAME);
assert_eq!(found_path, path);
assert_eq!(found_path, Some(path));
}
Err(err) => panic!("No errors were expected: {err}"),
}
Expand All @@ -340,7 +399,7 @@ mod tests {

let search = Search::find(&search_config, &sub).unwrap();

assert_eq!(search.justfile_path, justfile);
assert_eq!(search.justfile_path.unwrap(), justfile);
assert_eq!(search.working_directory, sub);
}

Expand Down
4 changes: 0 additions & 4 deletions src/search_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ use super::*;
pub(crate) enum SearchError {
#[snafu(display("Cannot initialize global justfile"))]
GlobalJustfileInit,
#[snafu(display("Global justfile not found"))]
GlobalJustfileNotFound,
#[snafu(display(
"I/O error reading directory `{}`: {}",
directory.display(),
Expand All @@ -16,8 +14,6 @@ pub(crate) enum SearchError {
directory: PathBuf,
io_error: io::Error,
},
#[snafu(display("Justfile path had no parent: {}", path.display()))]
JustfileHadNoParent { path: PathBuf },
#[snafu(display(
"Multiple candidate justfiles found in `{}`: {}",
candidates.iter().next().unwrap().parent().unwrap().display(),
Expand Down
Loading

0 comments on commit fb28b25

Please sign in to comment.