Lints and suggestions for the Nix programming language
1use std::{fmt::Write, iter::IntoIterator};
2
3use rnix::{
4 Root, SyntaxNode,
5 ast::{self, AstNode},
6};
7use rowan::ast::AstNode as _;
8
9fn ast_from_text<N: AstNode>(text: &str) -> N {
10 let parse = Root::parse(text).ok();
11
12 let Ok(parse) = parse else {
13 panic!("Failed to parse `{text:?}`")
14 };
15
16 let Some(node) = parse.syntax().descendants().find_map(N::cast) else {
17 panic!(
18 "Failed to make ast node `{}` from text `{}`",
19 std::any::type_name::<N>(),
20 text
21 );
22 };
23
24 node
25}
26
27pub fn parenthesize(node: &SyntaxNode) -> ast::Paren {
28 ast_from_text(&format!("({node})"))
29}
30
31pub fn quote(node: &SyntaxNode) -> ast::Str {
32 ast_from_text(&format!("\"{node}\""))
33}
34
35pub fn unary_not(node: &SyntaxNode) -> ast::UnaryOp {
36 ast_from_text(&format!("!{node}"))
37}
38
39pub fn inherit_stmt<'a>(nodes: impl IntoIterator<Item = &'a ast::Ident>) -> ast::Inherit {
40 let inherited_idents = nodes
41 .into_iter()
42 .map(std::string::ToString::to_string)
43 .collect::<Vec<_>>()
44 .join(" ");
45 ast_from_text(&format!("{{ inherit {inherited_idents}; }}"))
46}
47
48pub fn inherit_from_stmt<'a>(
49 from: &SyntaxNode,
50 nodes: impl IntoIterator<Item = &'a ast::Ident>,
51) -> ast::Inherit {
52 let inherited_idents = nodes
53 .into_iter()
54 .map(std::string::ToString::to_string)
55 .collect::<Vec<_>>()
56 .join(" ");
57 ast_from_text(&format!("{{ inherit ({from}) {inherited_idents}; }}"))
58}
59
60pub fn attrset(
61 inherits: impl IntoIterator<Item = ast::Inherit>,
62 entries: impl IntoIterator<Item = ast::Entry>,
63 recursive: bool,
64) -> ast::AttrSet {
65 let mut buffer = String::new();
66
67 writeln!(buffer, "{}{{", if recursive { "rec " } else { "" }).unwrap();
68 for inherit in inherits {
69 writeln!(buffer, " {inherit}").unwrap();
70 }
71 for entry in entries {
72 writeln!(buffer, " {entry}").unwrap();
73 }
74 write!(buffer, "}}").unwrap();
75
76 ast_from_text(&buffer)
77}
78
79pub fn select(set: &SyntaxNode, index: &SyntaxNode) -> ast::Select {
80 ast_from_text(&format!("{set}.{index}"))
81}
82
83pub fn ident(text: &str) -> ast::Ident {
84 ast_from_text(text)
85}
86
87// LATER: make `op` strongly typed here
88pub fn binary(lhs: &SyntaxNode, op: &str, rhs: &SyntaxNode) -> ast::BinOp {
89 ast_from_text(&format!("{lhs} {op} {rhs}"))
90}
91
92pub fn or_default(set: &SyntaxNode, index: &SyntaxNode, default: &SyntaxNode) -> ast::Select {
93 ast_from_text(&format!("{set}.{index} or {default}"))
94}