Skip to main content

slash_lang/parser/
ast.rs

1/// The root of a parsed slash-command program.
2///
3/// A program is a sequence of [`Pipeline`]s connected by `&&` / `||` operators.
4/// The operator stored on each pipeline indicates how it connects to the *next* one.
5#[derive(Debug, Clone, PartialEq)]
6pub struct Program {
7    pub pipelines: Vec<Pipeline>,
8}
9
10/// A sequence of [`Command`]s connected by `|` / `|&` operators within one logical unit.
11///
12/// `operator` indicates how this pipeline connects to the next one in the [`Program`]:
13/// - `Some(Op::And)` — next pipeline runs only if this one succeeds (`&&`)
14/// - `Some(Op::Or)` — next pipeline runs only if this one fails (`||`)
15/// - `None` — this is the last pipeline
16#[derive(Debug, Clone, PartialEq)]
17pub struct Pipeline {
18    pub commands: Vec<Command>,
19    pub operator: Option<Op>,
20}
21
22/// An operator connecting commands or pipelines.
23#[derive(Debug, Clone, PartialEq)]
24pub enum Op {
25    /// `&&` — run next pipeline only on success
26    And,
27    /// `||` — run next pipeline only on failure
28    Or,
29    /// `|` — pipe stdout to the next command
30    Pipe,
31    /// `|&` — pipe both stdout and stderr to the next command
32    PipeErr,
33}
34
35/// A single builder-chain argument on a command.
36///
37/// Parsed from dotted segments after the command name:
38/// `/cmd.flag` → `Arg { name: "flag", value: None }`
39/// `/cmd.key(val)` → `Arg { name: "key", value: Some("val") }`
40#[derive(Debug, Clone, PartialEq)]
41pub struct Arg {
42    pub name: String,
43    pub value: Option<String>,
44}
45
46/// A single parsed slash command.
47///
48/// # Priority
49///
50/// Inferred from the **shape** of the raw token before normalization:
51///
52/// | Token shape      | Priority |
53/// |------------------|----------|
54/// | `ALL_CAPS`       | Max      |
55/// | `TitleCase`      | High     |
56/// | `camelCase`      | Medium   |
57/// | `kebab-case`     | Low      |
58/// | `snake_case`     | Lowest   |
59///
60/// # Suffixes (applied outer → inner)
61///
62/// ```text
63/// /TitleCase+++???!  →  invalid (double ? is an error)
64/// /Deploy?!          →  name="deploy", optional, urgency=Low
65/// /ALERT!!!          →  urgency=High
66/// /verbose+++        →  verbosity=+3
67/// /quiet--           →  verbosity=-2
68/// ```
69#[derive(Debug, Clone, PartialEq)]
70pub struct Command {
71    /// The original token as it appeared in the input (e.g. `/Build.flag(v)!`).
72    pub raw: String,
73    /// Normalized lowercase name (e.g. `build`).
74    pub name: String,
75    /// Primary argument from `/cmd(value)` syntax.
76    pub primary: Option<String>,
77    /// Builder-chain arguments (e.g. `.flag(val).other`).
78    pub args: Vec<Arg>,
79    /// Priority inferred from the raw token's casing/separators.
80    pub priority: Priority,
81    /// Urgency from trailing `!` / `!!` / `!!!`.
82    pub urgency: Urgency,
83    /// Verbosity level from trailing `+` (positive) or `-` (negative) markers.
84    pub verbosity: i8,
85    /// Whether the command is optional (`?` suffix) — its return value may be absent.
86    pub optional: bool,
87    /// Numeric test identifier for `/test`-family commands (e.g. `/test3` → `Some(3)`).
88    pub test_id: Option<u32>,
89    /// Output redirection, if present. Closes the pipeline — no further `|` is allowed.
90    pub redirect: Option<Redirection>,
91    /// Pipe operator connecting this command to the next one within the same pipeline.
92    pub pipe: Option<Op>,
93}
94
95/// Priority level inferred from the command token's shape.
96#[derive(Debug, Clone, PartialEq)]
97pub enum Priority {
98    /// `ALL_CAPS` token
99    Max,
100    /// `TitleCase` token
101    High,
102    /// `camelCase` token
103    Medium,
104    /// `kebab-case` or plain lowercase token
105    Low,
106    /// `snake_case` token
107    Lowest,
108}
109
110/// Urgency level from trailing `!` markers.
111#[derive(Debug, Clone, PartialEq)]
112pub enum Urgency {
113    None,
114    /// `!`
115    Low,
116    /// `!!`
117    Medium,
118    /// `!!!`
119    High,
120}
121
122/// Output redirection target.
123#[derive(Debug, Clone, PartialEq)]
124pub enum Redirection {
125    /// `> file` — overwrite
126    Truncate(String),
127    /// `>> file` — append
128    Append(String),
129}
130
131impl Command {
132    pub fn new(name: String, priority: Priority) -> Self {
133        Self {
134            raw: String::new(),
135            name,
136            primary: None,
137            args: vec![],
138            priority,
139            urgency: Urgency::None,
140            verbosity: 0,
141            optional: false,
142            test_id: None,
143            redirect: None,
144            pipe: None,
145        }
146    }
147}