snix_cli_eval/
assignment.rs1use rnix::SyntaxKind;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub(crate) struct Assignment<'a> {
6 pub(crate) ident: &'a str,
7 pub(crate) value: rnix::ast::Expr,
8}
9
10impl<'a> Assignment<'a> {
11 pub fn parse(input: &'a str) -> Option<Self> {
18 let mut tt = rnix::tokenize(input);
19 macro_rules! next {
20 ($kind:ident) => {{
21 loop {
22 let (kind, tok) = tt.next()?;
23 if kind == SyntaxKind::TOKEN_WHITESPACE {
24 continue;
25 }
26 if kind != SyntaxKind::$kind {
27 return None;
28 }
29 break tok;
30 }
31 }};
32 }
33
34 let ident = next!(TOKEN_IDENT);
35 let equal = next!(TOKEN_ASSIGN);
36
37 let value_start = equal.as_ptr() as usize + equal.len() - input.as_ptr() as usize;
38 let value_expr = &input[value_start..];
39 let root = rnix::Root::parse(value_expr);
40 let value = root.tree().expr()?;
41
42 if !root.errors().is_empty() {
43 return None;
44 }
45
46 Some(Self { ident, value })
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn simple_assignments() {
56 for input in ["x = 4", "x = \t\t\n\t4", "x=4"] {
57 let res = Assignment::parse(input).unwrap();
58 assert_eq!(res.ident, "x");
59 assert_eq!(res.value.to_string(), "4");
60 }
61 }
62
63 #[test]
64 fn complex_exprs() {
65 let input = "x = { y = 4; z = let q = 7; in [ q (y // { z = 9; }) ]; }";
66 let res = Assignment::parse(input).unwrap();
67 assert_eq!(res.ident, "x");
68 }
69
70 #[test]
71 fn not_an_assignment() {
72 let input = "{ x = 4; }";
73 let res = Assignment::parse(input);
74 assert!(res.is_none(), "{input:?}");
75 }
76}