Skip to main content

just_lsp/
parameter.rs

1use super::*;
2
3#[derive(Debug, Clone, PartialEq)]
4pub enum ParameterKind {
5  Export,
6  Normal,
7  Variadic(VariadicType),
8}
9
10#[derive(Debug, Clone, PartialEq)]
11pub enum VariadicType {
12  OneOrMore,
13  ZeroOrMore,
14}
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct Parameter {
18  pub content: String,
19  pub default_value: Option<String>,
20  pub kind: ParameterKind,
21  pub name: String,
22  pub range: lsp::Range,
23}
24
25#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
26pub struct ParameterJson {
27  pub default_value: Option<String>,
28  pub name: String,
29}
30
31impl From<Parameter> for ParameterJson {
32  fn from(parameter: Parameter) -> Self {
33    ParameterJson {
34      name: parameter.name,
35      default_value: parameter.default_value,
36    }
37  }
38}
39
40impl Parameter {
41  #[must_use]
42  pub fn from_node(node: &Node, document: &Document) -> Option<Self> {
43    let (parameter_node, kind) = if node.kind() == "variadic_parameter" {
44      let kleene = document.get_node_text(&node.child_by_field_name("kleene")?);
45
46      let kind = match kleene.as_str() {
47        "+" => ParameterKind::Variadic(VariadicType::OneOrMore),
48        "*" => ParameterKind::Variadic(VariadicType::ZeroOrMore),
49        _ => return None,
50      };
51
52      (node.find("^parameter")?, kind)
53    } else {
54      let kind = if node.child(0).is_some_and(|child| child.kind() == "$") {
55        ParameterKind::Export
56      } else {
57        ParameterKind::Normal
58      };
59
60      (*node, kind)
61    };
62
63    let name =
64      document.get_node_text(&parameter_node.child_by_field_name("name")?);
65
66    let default_value = parameter_node
67      .child_by_field_name("default")
68      .map(|node| document.get_node_text(&node));
69
70    Some(Parameter {
71      name,
72      kind,
73      default_value,
74      content: document.get_node_text(node).trim().to_string(),
75      range: node.get_range(document),
76    })
77  }
78}
79
80#[cfg(test)]
81mod tests {
82  use {super::*, pretty_assertions::assert_eq};
83
84  #[test]
85  fn parse_normal_parameter() {
86    assert_eq!(
87      Document::from("foo target:\n").recipes()[0].parameters,
88      vec![Parameter {
89        name: "target".to_string(),
90        kind: ParameterKind::Normal,
91        default_value: None,
92        content: "target".to_string(),
93        range: lsp::Range::at(0, 4, 0, 10),
94      }],
95    );
96  }
97
98  #[test]
99  fn parse_parameter_with_default() {
100    assert_eq!(
101      Document::from("foo tests=\"default\":\n").recipes()[0].parameters,
102      vec![Parameter {
103        name: "tests".to_string(),
104        kind: ParameterKind::Normal,
105        default_value: Some("\"default\"".to_string()),
106        content: "tests=\"default\"".to_string(),
107        range: lsp::Range::at(0, 4, 0, 19),
108      }],
109    );
110  }
111
112  #[test]
113  fn parse_parameter_with_complex_default() {
114    assert_eq!(
115      Document::from("foo triple=(arch + \"-unknown-unknown\"):\n").recipes()
116        [0]
117        .parameters,
118      vec![Parameter {
119        name: "triple".to_string(),
120        kind: ParameterKind::Normal,
121        default_value: Some("(arch + \"-unknown-unknown\")".to_string()),
122        content: "triple=(arch + \"-unknown-unknown\")".to_string(),
123        range: lsp::Range::at(0, 4, 0, 38),
124      }],
125    );
126  }
127
128  #[test]
129  fn parse_export_parameter() {
130    assert_eq!(
131      Document::from("foo $bar:\n").recipes()[0].parameters,
132      vec![Parameter {
133        name: "bar".to_string(),
134        kind: ParameterKind::Export,
135        default_value: None,
136        content: "$bar".to_string(),
137        range: lsp::Range::at(0, 4, 0, 8),
138      }],
139    );
140  }
141
142  #[test]
143  fn parse_variadic_one_or_more_parameter() {
144    assert_eq!(
145      Document::from("foo +FILES:\n").recipes()[0].parameters,
146      vec![Parameter {
147        name: "FILES".to_string(),
148        kind: ParameterKind::Variadic(VariadicType::OneOrMore),
149        default_value: None,
150        content: "+FILES".to_string(),
151        range: lsp::Range::at(0, 4, 0, 10),
152      }],
153    );
154  }
155
156  #[test]
157  fn parse_variadic_zero_or_more_parameter() {
158    assert_eq!(
159      Document::from("foo *FLAGS:\n").recipes()[0].parameters,
160      vec![Parameter {
161        name: "FLAGS".to_string(),
162        kind: ParameterKind::Variadic(VariadicType::ZeroOrMore),
163        default_value: None,
164        content: "*FLAGS".to_string(),
165        range: lsp::Range::at(0, 4, 0, 10),
166      }],
167    );
168  }
169
170  #[test]
171  fn parse_variadic_with_default() {
172    assert_eq!(
173      Document::from("foo +FLAGS='-q':\n").recipes()[0].parameters,
174      vec![Parameter {
175        name: "FLAGS".to_string(),
176        kind: ParameterKind::Variadic(VariadicType::OneOrMore),
177        default_value: Some("'-q'".to_string()),
178        content: "+FLAGS='-q'".to_string(),
179        range: lsp::Range::at(0, 4, 0, 15),
180      }],
181    );
182  }
183
184  #[test]
185  fn invalid_parameter_input() {
186    assert_eq!(Document::from("foo:\n").recipes()[0].parameters, vec![]);
187    assert_eq!(Document::from("foo $:\n").recipes()[0].parameters, vec![]);
188    assert_eq!(Document::from("foo +:\n").recipes()[0].parameters, vec![]);
189    assert_eq!(Document::from("foo *:\n").recipes()[0].parameters, vec![]);
190  }
191}