Skip to main content

only_semantic/
ast.rs

1use smol_str::SmolStr;
2use text_size::TextRange;
3
4use crate::{GuardKind, ShellKind, ShellSelection};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct DocumentAst {
8    pub directives: Vec<DirectiveAst>,
9    pub namespaces: Vec<NamespaceAst>,
10    pub tasks: Vec<TaskAst>,
11    pub uses_braced_namespaces: bool,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum DirectiveAst {
16    Version {
17        major: u64,
18        minor: u64,
19        range: TextRange,
20    },
21    Shell {
22        shell: ShellKind,
23        range: TextRange,
24    },
25    Variable {
26        name: SmolStr,
27        value: SmolStr,
28        name_range: TextRange,
29        range: TextRange,
30    },
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct NamespaceAst {
35    pub name: SmolStr,
36    pub doc: Option<SmolStr>,
37    pub range: TextRange,
38    pub close_range: Option<TextRange>,
39    pub is_braced: bool,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct TaskAst {
44    pub name: SmolStr,
45    pub namespace: Option<SmolStr>,
46    pub doc: Option<SmolStr>,
47    pub params: Vec<ParamAst>,
48    pub guards: Vec<GuardAst>,
49    pub dependencies: Vec<DependencyAst>,
50    pub shell: Option<ShellAst>,
51    pub steps: Vec<TaskStepAst>,
52    pub range: TextRange,
53    pub uses_multiline_header: bool,
54}
55
56impl TaskAst {
57    pub fn qualified_name(&self) -> SmolStr {
58        match &self.namespace {
59            Some(namespace) => SmolStr::from(format!("{namespace}.{}", self.name)),
60            None => self.name.clone(),
61        }
62    }
63
64    pub fn signature(&self) -> SmolStr {
65        let mut signature = self.name.to_string();
66        signature.push('(');
67        signature.push_str(
68            &self
69                .params
70                .iter()
71                .map(
72                    |parameter| match (parameter.is_slice, &parameter.default_value) {
73                        (true, Some(default)) => format!("{}..=\"{default}\"", parameter.name),
74                        (true, None) => format!("{}..", parameter.name),
75                        (false, Some(default)) => format!("{}=\"{default}\"", parameter.name),
76                        (false, None) => parameter.name.to_string(),
77                    },
78                )
79                .collect::<Vec<_>>()
80                .join(", "),
81        );
82        signature.push(')');
83        SmolStr::from(signature)
84    }
85
86    /// Returns whether the task is a helper task hidden from normal CLI listings.
87    ///
88    /// Args:
89    /// None.
90    ///
91    /// Returns:
92    /// `true` when the task name starts with `_`.
93    pub fn is_helper(&self) -> bool {
94        self.name.starts_with('_')
95    }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct ParamAst {
100    pub name: SmolStr,
101    pub default_value: Option<SmolStr>,
102    pub is_slice: bool,
103    pub range: TextRange,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct GuardAst {
108    pub kind: GuardKind,
109    pub argument: SmolStr,
110    pub range: TextRange,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct ShellAst {
115    pub selection: ShellSelection,
116    pub range: TextRange,
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct DependencyAst {
121    pub name: SmolStr,
122    pub range: TextRange,
123    pub stage: usize,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct CommandAst {
128    pub text: SmolStr,
129    pub interpolations: Vec<InterpolationAst>,
130    pub range: TextRange,
131}
132
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub enum TaskStepAst {
135    Command(CommandAst),
136    CommandBlock(CommandBlockAst),
137}
138
139impl TaskStepAst {
140    pub fn source(&self) -> &str {
141        match self {
142            Self::Command(command) => command.text.as_str(),
143            Self::CommandBlock(block) => block.source.as_str(),
144        }
145    }
146
147    pub fn interpolations(&self) -> &[InterpolationAst] {
148        match self {
149            Self::Command(command) => &command.interpolations,
150            Self::CommandBlock(block) => &block.interpolations,
151        }
152    }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct CommandBlockAst {
157    pub source: SmolStr,
158    pub interpolations: Vec<InterpolationAst>,
159    pub range: TextRange,
160    pub line_ranges: Vec<TextRange>,
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct InterpolationAst {
165    pub name: SmolStr,
166    pub range: TextRange,
167}