From c2207a906bfdec4a478d06bd6eccad7692dd664c Mon Sep 17 00:00:00 2001
From: Casey Rodarmor <casey@rodarmor.com>
Date: Fri, 2 Aug 2024 12:20:07 -0700
Subject: [PATCH] Make function paths relative to correct working directory
 (#2294)

---
 src/function.rs    | 26 ++++----------
 tests/functions.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+), 19 deletions(-)

diff --git a/src/function.rs b/src/function.rs
index 013af683f8..a714a8d0fd 100644
--- a/src/function.rs
+++ b/src/function.rs
@@ -133,8 +133,7 @@ fn absolute_path(context: Context, path: &str) -> FunctionResult {
   let abs_path_unchecked = context
     .evaluator
     .context
-    .search
-    .working_directory
+    .working_directory()
     .join(path)
     .lexiclean();
   match abs_path_unchecked.to_str() {
@@ -164,12 +163,7 @@ fn blake3(_context: Context, s: &str) -> FunctionResult {
 }
 
 fn blake3_file(context: Context, path: &str) -> FunctionResult {
-  let path = context
-    .evaluator
-    .context
-    .search
-    .working_directory
-    .join(path);
+  let path = context.evaluator.context.working_directory().join(path);
   let mut hasher = blake3::Hasher::new();
   hasher
     .update_mmap_rayon(&path)
@@ -177,9 +171,9 @@ fn blake3_file(context: Context, path: &str) -> FunctionResult {
   Ok(hasher.finalize().to_string())
 }
 
-fn canonicalize(_context: Context, path: &str) -> FunctionResult {
-  let canonical =
-    std::fs::canonicalize(path).map_err(|err| format!("I/O error canonicalizing path: {err}"))?;
+fn canonicalize(context: Context, path: &str) -> FunctionResult {
+  let canonical = std::fs::canonicalize(context.evaluator.context.working_directory().join(path))
+    .map_err(|err| format!("I/O error canonicalizing path: {err}"))?;
 
   canonical.to_str().map(str::to_string).ok_or_else(|| {
     format!(
@@ -522,8 +516,7 @@ fn path_exists(context: Context, path: &str) -> FunctionResult {
     context
       .evaluator
       .context
-      .search
-      .working_directory
+      .working_directory()
       .join(path)
       .exists()
       .to_string(),
@@ -557,12 +550,7 @@ fn sha256(_context: Context, s: &str) -> FunctionResult {
 
 fn sha256_file(context: Context, path: &str) -> FunctionResult {
   use sha2::{Digest, Sha256};
-  let path = context
-    .evaluator
-    .context
-    .search
-    .working_directory
-    .join(path);
+  let path = context.evaluator.context.working_directory().join(path);
   let mut hasher = Sha256::new();
   let mut file =
     fs::File::open(&path).map_err(|err| format!("Failed to open `{}`: {err}", path.display()))?;
diff --git a/tests/functions.rs b/tests/functions.rs
index 4aaa61dd2c..76964b74b4 100644
--- a/tests/functions.rs
+++ b/tests/functions.rs
@@ -1093,3 +1093,93 @@ fn invocation_dir_native_abbreviation_is_accepted() {
     )
     .run();
 }
+
+#[test]
+fn absolute_path_argument_is_relative_to_submodule_working_directory() {
+  Test::new()
+    .justfile("mod foo")
+    .write("foo/baz", "")
+    .write(
+      "foo/mod.just",
+      r#"
+bar:
+  @echo "{{ absolute_path('baz') }}"
+
+"#,
+    )
+    .stdout_regex(r".*[/\\]foo[/\\]baz\n")
+    .args(["foo", "bar"])
+    .run();
+}
+
+#[test]
+fn blake3_file_argument_is_relative_to_submodule_working_directory() {
+  Test::new()
+    .justfile("mod foo")
+    .write("foo/baz", "")
+    .write(
+      "foo/mod.just",
+      "
+bar:
+  @echo {{ blake3_file('baz') }}
+
+",
+    )
+    .stdout("af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262\n")
+    .args(["foo", "bar"])
+    .run();
+}
+
+#[test]
+fn canonicalize_argument_is_relative_to_submodule_working_directory() {
+  Test::new()
+    .justfile("mod foo")
+    .write("foo/baz", "")
+    .write(
+      "foo/mod.just",
+      r#"
+bar:
+  @echo "{{ canonicalize('baz') }}"
+
+"#,
+    )
+    .stdout_regex(r".*[/\\]foo[/\\]baz\n")
+    .args(["foo", "bar"])
+    .run();
+}
+
+#[test]
+fn path_exists_argument_is_relative_to_submodule_working_directory() {
+  Test::new()
+    .justfile("mod foo")
+    .write("foo/baz", "")
+    .write(
+      "foo/mod.just",
+      "
+bar:
+  @echo {{ path_exists('baz') }}
+
+",
+    )
+    .stdout_regex("true\n")
+    .args(["foo", "bar"])
+    .run();
+}
+
+#[test]
+fn sha256_file_argument_is_relative_to_submodule_working_directory() {
+  Test::new()
+    .justfile("mod foo")
+    .write("foo/baz", "")
+    .write(
+      "foo/mod.just",
+      "
+bar:
+  @echo {{ sha256_file('baz') }}
+
+",
+    )
+    .stdout_regex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n")
+    .args(["foo", "bar"])
+    .run();
+}