Add module_file()
and module_directory()
functions (#2105)
This commit is contained in:
parent
b8044e789b
commit
db52d95146
@ -166,6 +166,7 @@ impl<'src> Analyzer<'src> {
|
||||
name,
|
||||
recipes,
|
||||
settings,
|
||||
source: root.into(),
|
||||
warnings,
|
||||
})
|
||||
}
|
||||
|
@ -4,9 +4,10 @@ pub(crate) struct Evaluator<'src: 'run, 'run> {
|
||||
pub(crate) assignments: Option<&'run Table<'src, Assignment<'src>>>,
|
||||
pub(crate) config: &'run Config,
|
||||
pub(crate) dotenv: &'run BTreeMap<String, String>,
|
||||
pub(crate) module_source: &'run Path,
|
||||
pub(crate) scope: Scope<'src, 'run>,
|
||||
pub(crate) settings: &'run Settings<'run>,
|
||||
pub(crate) search: &'run Search,
|
||||
pub(crate) settings: &'run Settings<'run>,
|
||||
}
|
||||
|
||||
impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
@ -14,17 +15,19 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
assignments: &'run Table<'src, Assignment<'src>>,
|
||||
config: &'run Config,
|
||||
dotenv: &'run BTreeMap<String, String>,
|
||||
overrides: Scope<'src, 'run>,
|
||||
settings: &'run Settings<'run>,
|
||||
module_source: &'run Path,
|
||||
scope: Scope<'src, 'run>,
|
||||
search: &'run Search,
|
||||
settings: &'run Settings<'run>,
|
||||
) -> RunResult<'src, Scope<'src, 'run>> {
|
||||
let mut evaluator = Self {
|
||||
scope: overrides,
|
||||
assignments: Some(assignments),
|
||||
config,
|
||||
dotenv,
|
||||
settings,
|
||||
module_source,
|
||||
scope,
|
||||
search,
|
||||
settings,
|
||||
};
|
||||
|
||||
for assignment in assignments.values() {
|
||||
@ -250,21 +253,23 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
}
|
||||
|
||||
pub(crate) fn evaluate_parameters(
|
||||
arguments: &[String],
|
||||
config: &'run Config,
|
||||
dotenv: &'run BTreeMap<String, String>,
|
||||
module_source: &'run Path,
|
||||
parameters: &[Parameter<'src>],
|
||||
arguments: &[String],
|
||||
scope: &'run Scope<'src, 'run>,
|
||||
settings: &'run Settings,
|
||||
search: &'run Search,
|
||||
settings: &'run Settings,
|
||||
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
||||
let mut evaluator = Self {
|
||||
assignments: None,
|
||||
config,
|
||||
dotenv,
|
||||
module_source,
|
||||
scope: scope.child(),
|
||||
search,
|
||||
settings,
|
||||
dotenv,
|
||||
config,
|
||||
};
|
||||
|
||||
let mut scope = scope.child();
|
||||
@ -307,17 +312,19 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
pub(crate) fn recipe_evaluator(
|
||||
config: &'run Config,
|
||||
dotenv: &'run BTreeMap<String, String>,
|
||||
module_source: &'run Path,
|
||||
scope: &'run Scope<'src, 'run>,
|
||||
settings: &'run Settings,
|
||||
search: &'run Search,
|
||||
) -> Evaluator<'src, 'run> {
|
||||
settings: &'run Settings,
|
||||
) -> Self {
|
||||
Self {
|
||||
assignments: None,
|
||||
config,
|
||||
dotenv,
|
||||
module_source,
|
||||
scope: Scope::child(scope),
|
||||
search,
|
||||
settings,
|
||||
dotenv,
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ pub(crate) fn get(name: &str) -> Option<Function> {
|
||||
"kebabcase" => Unary(kebabcase),
|
||||
"lowercamelcase" => Unary(lowercamelcase),
|
||||
"lowercase" => Unary(lowercase),
|
||||
"module_directory" => Nullary(module_directory),
|
||||
"module_file" => Nullary(module_file),
|
||||
"num_cpus" => Nullary(num_cpus),
|
||||
"os" => Nullary(os),
|
||||
"os_family" => Nullary(os_family),
|
||||
@ -406,6 +408,44 @@ fn lowercase(_context: Context, s: &str) -> Result<String, String> {
|
||||
Ok(s.to_lowercase())
|
||||
}
|
||||
|
||||
fn module_directory(context: Context) -> Result<String, String> {
|
||||
context
|
||||
.evaluator
|
||||
.search
|
||||
.justfile
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(context.evaluator.module_source)
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.map(str::to_owned)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Module directory is not valid unicode: {}",
|
||||
context.evaluator.module_source.parent().unwrap().display(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn module_file(context: Context) -> Result<String, String> {
|
||||
context
|
||||
.evaluator
|
||||
.search
|
||||
.justfile
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(context.evaluator.module_source)
|
||||
.to_str()
|
||||
.map(str::to_owned)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Module file path is not valid unicode: {}",
|
||||
context.evaluator.module_source.display(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn num_cpus(_context: Context) -> Result<String, String> {
|
||||
let num = num_cpus::get();
|
||||
Ok(num.to_string())
|
||||
|
@ -3,9 +3,10 @@ use {super::*, serde::Serialize};
|
||||
#[derive(Debug)]
|
||||
struct Invocation<'src: 'run, 'run> {
|
||||
arguments: Vec<&'run str>,
|
||||
module_source: &'run Path,
|
||||
recipe: &'run Recipe<'src>,
|
||||
settings: &'run Settings<'src>,
|
||||
scope: &'run Scope<'src, 'run>,
|
||||
settings: &'run Settings<'src>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
@ -21,6 +22,8 @@ pub(crate) struct Justfile<'src> {
|
||||
pub(crate) name: Option<Name<'src>>,
|
||||
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
|
||||
pub(crate) settings: Settings<'src>,
|
||||
#[serde(skip)]
|
||||
pub(crate) source: PathBuf,
|
||||
pub(crate) warnings: Vec<Warning>,
|
||||
}
|
||||
|
||||
@ -106,9 +109,10 @@ impl<'src> Justfile<'src> {
|
||||
&self.assignments,
|
||||
config,
|
||||
dotenv,
|
||||
&self.source,
|
||||
scope,
|
||||
&self.settings,
|
||||
search,
|
||||
&self.settings,
|
||||
)
|
||||
}
|
||||
|
||||
@ -276,10 +280,12 @@ impl<'src> Justfile<'src> {
|
||||
let mut ran = Ran::default();
|
||||
for invocation in invocations {
|
||||
let context = RecipeContext {
|
||||
settings: invocation.settings,
|
||||
config,
|
||||
dotenv: &dotenv,
|
||||
module_source: invocation.module_source,
|
||||
scope: invocation.scope,
|
||||
search,
|
||||
settings: invocation.settings,
|
||||
};
|
||||
|
||||
Self::run_recipe(
|
||||
@ -290,7 +296,6 @@ impl<'src> Justfile<'src> {
|
||||
.map(str::to_string)
|
||||
.collect::<Vec<String>>(),
|
||||
&context,
|
||||
&dotenv,
|
||||
&mut ran,
|
||||
invocation.recipe,
|
||||
search,
|
||||
@ -346,6 +351,7 @@ impl<'src> Justfile<'src> {
|
||||
recipe,
|
||||
arguments: Vec::new(),
|
||||
scope,
|
||||
module_source: &self.source,
|
||||
},
|
||||
depth,
|
||||
)));
|
||||
@ -373,6 +379,7 @@ impl<'src> Justfile<'src> {
|
||||
recipe,
|
||||
scope: parent,
|
||||
settings: &self.settings,
|
||||
module_source: &self.source,
|
||||
},
|
||||
depth,
|
||||
)))
|
||||
@ -394,6 +401,7 @@ impl<'src> Justfile<'src> {
|
||||
recipe,
|
||||
scope: parent,
|
||||
settings: &self.settings,
|
||||
module_source: &self.source,
|
||||
},
|
||||
depth + argument_count,
|
||||
)))
|
||||
@ -410,7 +418,6 @@ impl<'src> Justfile<'src> {
|
||||
fn run_recipe(
|
||||
arguments: &[String],
|
||||
context: &RecipeContext<'src, '_>,
|
||||
dotenv: &BTreeMap<String, String>,
|
||||
ran: &mut Ran<'src>,
|
||||
recipe: &Recipe<'src>,
|
||||
search: &Search,
|
||||
@ -426,19 +433,26 @@ impl<'src> Justfile<'src> {
|
||||
}
|
||||
|
||||
let (outer, positional) = Evaluator::evaluate_parameters(
|
||||
context.config,
|
||||
dotenv,
|
||||
&recipe.parameters,
|
||||
arguments,
|
||||
context.config,
|
||||
context.dotenv,
|
||||
context.module_source,
|
||||
&recipe.parameters,
|
||||
context.scope,
|
||||
context.settings,
|
||||
search,
|
||||
context.settings,
|
||||
)?;
|
||||
|
||||
let scope = outer.child();
|
||||
|
||||
let mut evaluator =
|
||||
Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
|
||||
let mut evaluator = Evaluator::recipe_evaluator(
|
||||
context.config,
|
||||
context.dotenv,
|
||||
context.module_source,
|
||||
&scope,
|
||||
search,
|
||||
context.settings,
|
||||
);
|
||||
|
||||
if !context.config.no_dependencies {
|
||||
for Dependency { recipe, arguments } in recipe.dependencies.iter().take(recipe.priors) {
|
||||
@ -447,11 +461,11 @@ impl<'src> Justfile<'src> {
|
||||
.map(|argument| evaluator.evaluate_expression(argument))
|
||||
.collect::<RunResult<Vec<String>>>()?;
|
||||
|
||||
Self::run_recipe(&arguments, context, dotenv, ran, recipe, search)?;
|
||||
Self::run_recipe(&arguments, context, ran, recipe, search)?;
|
||||
}
|
||||
}
|
||||
|
||||
recipe.run(context, dotenv, scope.child(), search, &positional)?;
|
||||
recipe.run(context, &scope, &positional)?;
|
||||
|
||||
if !context.config.no_dependencies {
|
||||
let mut ran = Ran::default();
|
||||
@ -463,11 +477,12 @@ impl<'src> Justfile<'src> {
|
||||
evaluated.push(evaluator.evaluate_expression(argument)?);
|
||||
}
|
||||
|
||||
Self::run_recipe(&evaluated, context, dotenv, &mut ran, recipe, search)?;
|
||||
Self::run_recipe(&evaluated, context, &mut ran, recipe, search)?;
|
||||
}
|
||||
}
|
||||
|
||||
ran.ran(&recipe.namepath, arguments.to_vec());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,11 @@ use {super::*, TokenKind::*};
|
||||
/// All methods starting with `parse_*` parse and return a language construct.
|
||||
///
|
||||
/// The parser tracks an expected set of tokens as it parses. This set contains
|
||||
/// all tokens which would have been accepted at the current point in the parse.
|
||||
/// Whenever the parser tests for a token that would be accepted, but does not
|
||||
/// find it, it adds that token to the set. When the parser accepts a token, the
|
||||
/// set is cleared. If the parser finds a token which is unexpected, the
|
||||
/// contents of the set is printed in the resultant error message.
|
||||
/// all tokens which would have been accepted at the current point in the
|
||||
/// parse. Whenever the parser tests for a token that would be accepted, but
|
||||
/// does not find it, it adds that token to the set. When the parser accepts a
|
||||
/// token, the set is cleared. If the parser finds a token which is unexpected,
|
||||
/// the elements of the set are printed in the resultant error message.
|
||||
pub(crate) struct Parser<'run, 'src> {
|
||||
expected_tokens: BTreeSet<TokenKind>,
|
||||
file_depth: u32,
|
||||
|
@ -147,9 +147,7 @@ impl<'src, D> Recipe<'src, D> {
|
||||
pub(crate) fn run<'run>(
|
||||
&self,
|
||||
context: &RecipeContext<'src, 'run>,
|
||||
dotenv: &BTreeMap<String, String>,
|
||||
scope: Scope<'src, 'run>,
|
||||
search: &'run Search,
|
||||
scope: &Scope<'src, 'run>,
|
||||
positional: &[String],
|
||||
) -> RunResult<'src, ()> {
|
||||
let config = &context.config;
|
||||
@ -164,20 +162,25 @@ impl<'src, D> Recipe<'src, D> {
|
||||
);
|
||||
}
|
||||
|
||||
let evaluator =
|
||||
Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
|
||||
let evaluator = Evaluator::recipe_evaluator(
|
||||
context.config,
|
||||
context.dotenv,
|
||||
context.module_source,
|
||||
scope,
|
||||
context.search,
|
||||
context.settings,
|
||||
);
|
||||
|
||||
if self.shebang {
|
||||
self.run_shebang(context, dotenv, &scope, positional, config, evaluator)
|
||||
self.run_shebang(context, scope, positional, config, evaluator)
|
||||
} else {
|
||||
self.run_linewise(context, dotenv, &scope, positional, config, evaluator)
|
||||
self.run_linewise(context, scope, positional, config, evaluator)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_linewise<'run>(
|
||||
&self,
|
||||
context: &RecipeContext<'src, 'run>,
|
||||
dotenv: &BTreeMap<String, String>,
|
||||
scope: &Scope<'src, 'run>,
|
||||
positional: &[String],
|
||||
config: &Config,
|
||||
@ -264,7 +267,7 @@ impl<'src, D> Recipe<'src, D> {
|
||||
cmd.stdout(Stdio::null());
|
||||
}
|
||||
|
||||
cmd.export(context.settings, dotenv, scope);
|
||||
cmd.export(context.settings, context.dotenv, scope);
|
||||
|
||||
match InterruptHandler::guard(|| cmd.status()) {
|
||||
Ok(exit_status) => {
|
||||
@ -298,7 +301,6 @@ impl<'src, D> Recipe<'src, D> {
|
||||
pub(crate) fn run_shebang<'run>(
|
||||
&self,
|
||||
context: &RecipeContext<'src, 'run>,
|
||||
dotenv: &BTreeMap<String, String>,
|
||||
scope: &Scope<'src, 'run>,
|
||||
positional: &[String],
|
||||
config: &Config,
|
||||
@ -411,7 +413,7 @@ impl<'src, D> Recipe<'src, D> {
|
||||
command.args(positional);
|
||||
}
|
||||
|
||||
command.export(context.settings, dotenv, scope);
|
||||
command.export(context.settings, context.dotenv, scope);
|
||||
|
||||
// run it!
|
||||
match InterruptHandler::guard(|| command.status()) {
|
||||
|
@ -2,6 +2,8 @@ use super::*;
|
||||
|
||||
pub(crate) struct RecipeContext<'src: 'run, 'run> {
|
||||
pub(crate) config: &'run Config,
|
||||
pub(crate) dotenv: &'run BTreeMap<String, String>,
|
||||
pub(crate) module_source: &'run Path,
|
||||
pub(crate) scope: &'run Scope<'src, 'run>,
|
||||
pub(crate) search: &'run Search,
|
||||
pub(crate) settings: &'run Settings<'src>,
|
||||
|
@ -904,3 +904,126 @@ fn source_directory() {
|
||||
.stdout_regex(r".*[/\\]foo\n")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_paths() {
|
||||
Test::new()
|
||||
.write(
|
||||
"foo/bar.just",
|
||||
"
|
||||
imf := module_file()
|
||||
imd := module_directory()
|
||||
|
||||
import-outer: import-inner
|
||||
|
||||
@import-inner pmf=module_file() pmd=module_directory():
|
||||
echo import
|
||||
echo '{{ imf }}'
|
||||
echo '{{ imd }}'
|
||||
echo '{{ pmf }}'
|
||||
echo '{{ pmd }}'
|
||||
echo '{{ module_file() }}'
|
||||
echo '{{ module_directory() }}'
|
||||
",
|
||||
)
|
||||
.write(
|
||||
"baz/mod.just",
|
||||
"
|
||||
import 'foo/bar.just'
|
||||
|
||||
mmf := module_file()
|
||||
mmd := module_directory()
|
||||
|
||||
outer: inner
|
||||
|
||||
@inner pmf=module_file() pmd=module_directory():
|
||||
echo module
|
||||
echo '{{ mmf }}'
|
||||
echo '{{ mmd }}'
|
||||
echo '{{ pmf }}'
|
||||
echo '{{ pmd }}'
|
||||
echo '{{ module_file() }}'
|
||||
echo '{{ module_directory() }}'
|
||||
",
|
||||
)
|
||||
.write(
|
||||
"baz/foo/bar.just",
|
||||
"
|
||||
imf := module_file()
|
||||
imd := module_directory()
|
||||
|
||||
import-outer: import-inner
|
||||
|
||||
@import-inner pmf=module_file() pmd=module_directory():
|
||||
echo import
|
||||
echo '{{ imf }}'
|
||||
echo '{{ imd }}'
|
||||
echo '{{ pmf }}'
|
||||
echo '{{ pmd }}'
|
||||
echo '{{ module_file() }}'
|
||||
echo '{{ module_directory() }}'
|
||||
",
|
||||
)
|
||||
.justfile(
|
||||
"
|
||||
import 'foo/bar.just'
|
||||
mod baz
|
||||
|
||||
rmf := module_file()
|
||||
rmd := module_directory()
|
||||
|
||||
outer: inner
|
||||
|
||||
@inner pmf=module_file() pmd=module_directory():
|
||||
echo root
|
||||
echo '{{ rmf }}'
|
||||
echo '{{ rmd }}'
|
||||
echo '{{ pmf }}'
|
||||
echo '{{ pmd }}'
|
||||
echo '{{ module_file() }}'
|
||||
echo '{{ module_directory() }}'
|
||||
",
|
||||
)
|
||||
.test_round_trip(false)
|
||||
.args([
|
||||
"--unstable",
|
||||
"outer",
|
||||
"import-outer",
|
||||
"baz",
|
||||
"outer",
|
||||
"baz",
|
||||
"import-outer",
|
||||
])
|
||||
.stdout_regex(
|
||||
r"root
|
||||
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||
.*[/\\]just-test-tempdir......
|
||||
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||
.*[/\\]just-test-tempdir......
|
||||
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||
.*[/\\]just-test-tempdir......
|
||||
import
|
||||
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||
.*[/\\]just-test-tempdir......
|
||||
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||
.*[/\\]just-test-tempdir......
|
||||
.*[/\\]just-test-tempdir......[/\\]justfile
|
||||
.*[/\\]just-test-tempdir......
|
||||
module
|
||||
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||
.*[/\\]just-test-tempdir......[/\\]baz
|
||||
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||
.*[/\\]just-test-tempdir......[/\\]baz
|
||||
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||
.*[/\\]just-test-tempdir......[/\\]baz
|
||||
import
|
||||
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||
.*[/\\]just-test-tempdir......[/\\]baz
|
||||
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||
.*[/\\]just-test-tempdir......[/\\]baz
|
||||
.*[/\\]just-test-tempdir......[/\\]baz[/\\]mod.just
|
||||
.*[/\\]just-test-tempdir......[/\\]baz
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user