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