1use std::error::Error;
4
5use hcl::{Block, Body, Expression, Identifier, ObjectKey};
6
7use crate::parser::Field;
8
9pub enum QueryResult {
11 Expr(Expression),
13 Block(Block),
15}
16
17impl QueryResult {
18 pub fn to_string(&self) -> Result<String, Box<dyn Error>> {
19 let s = match self {
20 Self::Expr(expr) => hcl::format::to_string(expr)?,
23 Self::Block(block) => hcl::format::to_string(block)?,
24 };
25 Ok(s)
26 }
27}
28
29pub fn query(fields: &mut Vec<Field>, body: &Body) -> Vec<QueryResult> {
34 if fields.is_empty() {
35 unreachable!();
37 }
38
39 let field = fields.remove(0);
42 let mut query_result = body_query(&field, body);
43
44 while !fields.is_empty() {
47 let field = fields.remove(0);
48 query_result = result_query(&field, query_result);
49 }
50
51 query_result
52}
53
54fn body_query(field: &Field, body: &Body) -> Vec<QueryResult> {
55 let mut matches = Vec::new();
56 let mut attr_matches = attr_query(&field.name, body);
57 matches.append(&mut attr_matches);
58 let mut block_matches = block_query(field, body);
59 matches.append(&mut block_matches);
60 matches
61}
62
63fn attr_query(field: &str, body: &Body) -> Vec<QueryResult> {
64 let mut matches = Vec::new();
65 for attr in body.attributes() {
66 if attr.key() == field {
67 matches.push(QueryResult::Expr(attr.expr().clone()));
68 }
69 }
70 matches
71}
72
73fn block_query(field: &Field, body: &Body) -> Vec<QueryResult> {
74 let mut matches = Vec::new();
75 for block in body.blocks() {
76 if block.identifier() != field.name {
77 continue;
78 }
79 if field.labels.is_empty() {
80 matches.push(QueryResult::Block(block.clone()));
81 }
82 for filter_label in &field.labels {
83 for block_label in block.labels() {
84 if block_label.as_str() == filter_label {
85 matches.push(QueryResult::Block(block.clone()));
86 }
87 }
88 }
89 }
90 matches
91}
92
93fn result_query(field: &Field, query_results: Vec<QueryResult>) -> Vec<QueryResult> {
94 let mut matches = Vec::new();
95 for query_result in query_results {
96 match query_result {
97 QueryResult::Expr(expr) => {
98 if let Expression::Object(object) = expr {
99 if let Ok(id) = Identifier::new(&field.name) {
101 let key = ObjectKey::Identifier(id);
102 if let Some(expr) = object.get(&key) {
103 matches.push(QueryResult::Expr(expr.clone()));
104 }
105 }
106 let key = ObjectKey::Expression(Expression::String(field.name.clone()));
108 if let Some(expr) = object.get(&key) {
109 matches.push(QueryResult::Expr(expr.clone()));
110 }
111 }
112 }
113 QueryResult::Block(block) => {
114 let mut body_matches = body_query(field, block.body());
115 matches.append(&mut body_matches);
116 }
117 }
118 }
119 matches
120}