use super::*;
pub(crate) trait Node<'src> {
fn tree(&self) -> Tree<'src>;
}
impl<'src> Node<'src> for Ast<'src> {
fn tree(&self) -> Tree<'src> {
Tree::atom("justfile")
.extend(
self
.items
.iter()
.filter(|item| !matches!(item, Item::Newline))
.map(Node::tree),
)
.extend(self.warnings.iter().map(Node::tree))
}
}
impl<'src> Node<'src> for Item<'src> {
fn tree(&self) -> Tree<'src> {
match self {
Self::Alias(alias) => alias.tree(),
Self::Assignment(assignment) => assignment.tree(),
Self::Comment(comment) => comment.tree(),
Self::Import {
relative, optional, ..
} => {
let mut tree = Tree::atom("import");
if *optional {
tree = tree.push("?");
}
tree.push(format!("{relative}"))
}
Self::Module {
name,
optional,
relative,
..
} => {
let mut tree = Tree::atom("mod");
if *optional {
tree = tree.push("?");
}
tree = tree.push(name.lexeme());
if let Some(relative) = relative {
tree = tree.push(format!("{relative}"));
}
tree
}
Self::Newline => unreachable!(),
Self::Function(function) => {
let mut tree = Tree::atom("function");
tree.push_mut(function.name.lexeme());
for (name, _number) in &function.parameters {
tree.push_mut(name.lexeme());
}
tree.push_mut(function.body.tree());
tree
}
Self::Recipe(recipe) => recipe.tree(),
Self::Set(set) => set.tree(),
Self::Unexport { name } => {
let mut unexport = Tree::atom(Keyword::Unexport.lexeme());
unexport.push_mut(name.lexeme().replace('-', "_"));
unexport
}
}
}
}
impl<'src> Node<'src> for Namepath<'src> {
fn tree(&self) -> Tree<'src> {
match self.components() {
1 => Tree::atom(self.last().lexeme()),
_ => Tree::list(
self
.iter()
.map(|name| Tree::atom(Cow::Borrowed(name.lexeme()))),
),
}
}
}
impl<'src> Node<'src> for Alias<'src, Namepath<'src>> {
fn tree(&self) -> Tree<'src> {
let target = self.target.tree();
Tree::atom(Keyword::Alias.lexeme())
.push(self.name.lexeme())
.push(target)
}
}
impl<'src> Node<'src> for Assignment<'src> {
fn tree(&self) -> Tree<'src> {
if self.export {
Tree::atom("assignment")
.push("#")
.push(Keyword::Export.lexeme())
} else {
Tree::atom("assignment")
}
.push(self.name.lexeme())
.push(self.value.tree())
}
}
impl<'src> Node<'src> for Expression<'src> {
fn tree(&self) -> Tree<'src> {
match self {
Self::And { lhs, rhs } => Tree::atom("&&").push(lhs.tree()).push(rhs.tree()),
Self::Assert {
condition: Condition { lhs, rhs, operator },
error,
..
} => Tree::atom(Keyword::Assert.lexeme())
.push(lhs.tree())
.push(operator.to_string())
.push(rhs.tree())
.push(error.tree()),
Self::Backtick { contents, .. } => Tree::atom("backtick").push(Tree::string(contents)),
Self::Call { name, arguments } => {
let mut tree = Tree::atom("call");
tree.push_mut(name.lexeme());
for arg in arguments {
tree.push_mut(arg.tree());
}
tree
}
Self::Concatenation { lhs, rhs } => Tree::atom("+").push(lhs.tree()).push(rhs.tree()),
Self::Conditional {
condition: Condition { lhs, rhs, operator },
then,
otherwise,
} => {
let mut tree = Tree::atom(Keyword::If.lexeme());
tree.push_mut(lhs.tree());
tree.push_mut(operator.to_string());
tree.push_mut(rhs.tree());
tree.push_mut(then.tree());
tree.push_mut(otherwise.tree());
tree
}
Self::FormatString { start, expressions } => {
let mut tree = Tree::atom("format");
tree.push_mut(Tree::string(&start.cooked));
for (expression, string) in expressions {
tree.push_mut(expression.tree());
tree.push_mut(Tree::string(&string.cooked));
}
tree
}
Self::Group { contents } => Tree::List(vec![contents.tree()]),
Self::Join { lhs: None, rhs } => Tree::atom("/").push(rhs.tree()),
Self::Join {
lhs: Some(lhs),
rhs,
} => Tree::atom("/").push(lhs.tree()).push(rhs.tree()),
Self::Or { lhs, rhs } => Tree::atom("||").push(lhs.tree()).push(rhs.tree()),
Self::StringLiteral {
string_literal: StringLiteral { cooked, .. },
} => Tree::string(cooked),
Self::Variable { name } => Tree::atom(name.lexeme()),
}
}
}
impl<'src> Node<'src> for UnresolvedRecipe<'src> {
fn tree(&self) -> Tree<'src> {
let mut t = Tree::atom("recipe");
if self.quiet {
t.push_mut("#");
t.push_mut("quiet");
}
if let Some(doc) = &self.doc {
t.push_mut(Tree::string(doc));
}
t.push_mut(self.name.lexeme());
if !self.parameters.is_empty() {
let mut params = Tree::atom("params");
for parameter in &self.parameters {
if let Some(prefix) = parameter.kind.prefix() {
params.push_mut(prefix);
}
params.push_mut(parameter.tree());
}
t.push_mut(params);
}
if !self.dependencies.is_empty() {
let mut dependencies = Tree::atom("deps");
let mut subsequents = Tree::atom("sups");
for (i, dependency) in self.dependencies.iter().enumerate() {
let mut d = dependency.recipe.tree();
for argument in &dependency.arguments {
d.push_mut(argument.tree());
}
if i < self.priors {
dependencies.push_mut(d);
} else {
subsequents.push_mut(d);
}
}
if let Tree::List(_) = dependencies {
t.push_mut(dependencies);
}
if let Tree::List(_) = subsequents {
t.push_mut(subsequents);
}
}
if !self.body.is_empty() {
t.push_mut(Tree::atom("body").extend(self.body.iter().map(Node::tree)));
}
t
}
}
impl<'src> Node<'src> for Parameter<'src> {
fn tree(&self) -> Tree<'src> {
let mut children = vec![Tree::atom(self.name.lexeme())];
if let Some(default) = &self.default {
children.push(default.tree());
}
Tree::List(children)
}
}
impl<'src> Node<'src> for Line<'src> {
fn tree(&self) -> Tree<'src> {
Tree::list(self.fragments.iter().map(Node::tree))
}
}
impl<'src> Node<'src> for Fragment<'src> {
fn tree(&self) -> Tree<'src> {
match self {
Self::Text { token } => Tree::string(token.lexeme()),
Self::Interpolation { expression } => Tree::List(vec![expression.tree()]),
}
}
}
impl<'src> Node<'src> for Set<'src> {
fn tree(&self) -> Tree<'src> {
let mut set = Tree::atom(Keyword::Set.lexeme());
set.push_mut(self.name.lexeme().replace('-', "_"));
match &self.value {
Setting::AllowDuplicateRecipes(value)
| Setting::AllowDuplicateVariables(value)
| Setting::DotenvLoad(value)
| Setting::DotenvOverride(value)
| Setting::DotenvRequired(value)
| Setting::Export(value)
| Setting::Fallback(value)
| Setting::Guards(value)
| Setting::IgnoreComments(value)
| Setting::Lazy(value)
| Setting::NoExitMessage(value)
| Setting::PositionalArguments(value)
| Setting::Quiet(value)
| Setting::Unstable(value)
| Setting::WindowsPowerShell(value) => {
set.push_mut(value.to_string());
}
Setting::DotenvFilename(value)
| Setting::DotenvPath(value)
| Setting::Tempdir(value)
| Setting::WorkingDirectory(value) => {
set.push_mut(value.tree());
}
Setting::ScriptInterpreter(Interpreter { command, arguments })
| Setting::Shell(Interpreter { command, arguments })
| Setting::WindowsShell(Interpreter { command, arguments }) => {
set.push_mut(command.tree());
for argument in arguments {
set.push_mut(argument.tree());
}
}
}
set
}
}
impl<'src> Node<'src> for Warning {
fn tree(&self) -> Tree<'src> {
unreachable!()
}
}
impl<'src> Node<'src> for str {
fn tree(&self) -> Tree<'src> {
Tree::atom("comment").push(["\"", self, "\""].concat())
}
}