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 metadata: TaskMetadataAst,
38    pub range: TextRange,
39    pub close_range: Option<TextRange>,
40    pub is_braced: bool,
41    pub is_group: bool,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct TaskAst {
46    pub name: SmolStr,
47    pub namespace: Option<SmolStr>,
48    /// Legacy alias for the task help text.
49    pub doc: Option<SmolStr>,
50    pub metadata: TaskMetadataAst,
51    pub params: Vec<ParamAst>,
52    pub guards: Vec<GuardAst>,
53    pub dependencies: Vec<DependencyAst>,
54    pub shell: Option<ShellAst>,
55    pub steps: Vec<TaskStepAst>,
56    pub range: TextRange,
57    pub uses_multiline_header: bool,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Default)]
61pub struct TaskMetadataAst {
62    pub help: Option<SmolStr>,
63    pub help_count: usize,
64    pub desc: Option<SmolStr>,
65    pub pass: Option<SmolStr>,
66    pub fail: Option<SmolStr>,
67    pub unknown_fields: Vec<SmolStr>,
68    pub has_structured_fields: bool,
69}
70
71impl TaskAst {
72    pub fn qualified_name(&self) -> SmolStr {
73        match &self.namespace {
74            Some(namespace) => SmolStr::from(format!("{namespace}.{}", self.name)),
75            None => self.name.clone(),
76        }
77    }
78
79    pub fn signature(&self) -> SmolStr {
80        let mut signature = self.name.to_string();
81        signature.push('(');
82        signature.push_str(
83            &self
84                .params
85                .iter()
86                .map(
87                    |parameter| match (parameter.is_slice, &parameter.default_value) {
88                        (true, Some(default)) => format!("{}..=\"{default}\"", parameter.name),
89                        (true, None) => format!("{}..", parameter.name),
90                        (false, Some(default)) => format!("{}=\"{default}\"", parameter.name),
91                        (false, None) => parameter.name.to_string(),
92                    },
93                )
94                .collect::<Vec<_>>()
95                .join(", "),
96        );
97        signature.push(')');
98        SmolStr::from(signature)
99    }
100
101    /// Returns whether the task is a helper task hidden from normal CLI listings.
102    ///
103    /// Args:
104    /// None.
105    ///
106    /// Returns:
107    /// `true` when the task name starts with `_`.
108    pub fn is_helper(&self) -> bool {
109        self.name.starts_with('_')
110    }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct ParamAst {
115    pub name: SmolStr,
116    pub default_value: Option<SmolStr>,
117    pub is_slice: bool,
118    pub range: TextRange,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct GuardAst {
123    pub kind: GuardKind,
124    pub argument: SmolStr,
125    pub range: TextRange,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct ShellAst {
130    pub selection: ShellSelection,
131    pub range: TextRange,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct DependencyAst {
136    pub name: SmolStr,
137    pub range: TextRange,
138    pub arguments: Vec<DependencyArgumentAst>,
139    pub invocation_range: TextRange,
140    pub stage: usize,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq)]
144pub struct DependencyArgumentAst {
145    pub value: SmolStr,
146    pub range: TextRange,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
150pub struct CommandAst {
151    pub text: SmolStr,
152    pub interpolations: Vec<InterpolationAst>,
153    pub range: TextRange,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub enum TaskStepAst {
158    Command(CommandAst),
159    CommandBlock(CommandBlockAst),
160}
161
162impl TaskStepAst {
163    pub fn source(&self) -> &str {
164        match self {
165            Self::Command(command) => command.text.as_str(),
166            Self::CommandBlock(block) => block.source.as_str(),
167        }
168    }
169
170    pub fn interpolations(&self) -> &[InterpolationAst] {
171        match self {
172            Self::Command(command) => &command.interpolations,
173            Self::CommandBlock(block) => &block.interpolations,
174        }
175    }
176}
177
178#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct CommandBlockAst {
180    pub source: SmolStr,
181    pub interpolations: Vec<InterpolationAst>,
182    pub range: TextRange,
183    pub line_ranges: Vec<TextRange>,
184}
185
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct InterpolationAst {
188    pub name: SmolStr,
189    pub range: TextRange,
190}