use crate::{ast, rules::NodeRule}; pub struct ListToAttrsMap; impl NodeRule for ListToAttrsMap { type Node = ast::Binding; const ID: &'static str = "list_to_attrs_map"; fn select(data: &ast::NodeData) -> Option<&Self::Node> { let ast::NodeData::Binding(binding) = data; Some(binding) } fn check(node: &Self::Node) -> Option<&'static str> { matches_list_to_attrs_map(&node.value) .then_some("Use `lib.genAttrs'` instead of `lib.listToAttrs` after `map`") } } fn matches_list_to_attrs_map(expr: &ast::Expr) -> bool { let ast::Expr::Apply(application) = unwrap_expr(expr) else { return false; }; is_lib_list_to_attrs(&application.lambda) && is_map_application(&application.argument) } fn unwrap_expr(expr: &ast::Expr) -> &ast::Expr { match expr { ast::Expr::Other(expressions) if expressions.len() == 1 => unwrap_expr(&expressions[0]), _ => expr, } } fn is_lib_list_to_attrs(expr: &ast::Expr) -> bool { matches!( unwrap_expr(expr), ast::Expr::Select(select) if select.receiver == "lib" && select.attr.name == "listToAttrs" ) } fn is_map_application(expr: &ast::Expr) -> bool { let ast::Expr::Apply(application) = unwrap_expr(expr) else { return false; }; is_map(&application.lambda) } fn is_map(expr: &ast::Expr) -> bool { match unwrap_expr(expr) { ast::Expr::Ident(ident) => ident.name == "map", ast::Expr::Apply(application) => is_map(&application.lambda), _ => false, } }