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