use std::error::Error;
use hcl::{Block, Body, Expression, Identifier, ObjectKey};
use crate::parser::Field;
pub enum QueryResult {
Expr(Expression),
Block(Block),
}
impl QueryResult {
pub fn to_string(&self) -> Result<String, Box<dyn Error>> {
let s = match self {
Self::Expr(expr) => hcl::format::to_string(expr)?,
Self::Block(block) => hcl::format::to_string(block)?,
};
Ok(s)
}
}
pub fn query(fields: &mut Vec<Field>, body: &Body) -> Vec<QueryResult> {
if fields.is_empty() {
unreachable!();
}
let field = fields.remove(0);
let mut query_result = body_query(&field, body);
while !fields.is_empty() {
let field = fields.remove(0);
query_result = result_query(&field, query_result);
}
query_result
}
fn body_query(field: &Field, body: &Body) -> Vec<QueryResult> {
let mut matches = Vec::new();
let mut attr_matches = attr_query(&field.name, body);
matches.append(&mut attr_matches);
let mut block_matches = block_query(field, body);
matches.append(&mut block_matches);
matches
}
fn attr_query(field: &str, body: &Body) -> Vec<QueryResult> {
let mut matches = Vec::new();
for attr in body.attributes() {
if attr.key() == field {
matches.push(QueryResult::Expr(attr.expr().clone()));
}
}
matches
}
fn block_query(field: &Field, body: &Body) -> Vec<QueryResult> {
let mut matches = Vec::new();
for block in body.blocks() {
if block.identifier() != field.name {
continue;
}
if field.labels.is_empty() {
matches.push(QueryResult::Block(block.clone()));
}
for filter_label in &field.labels {
for block_label in block.labels() {
if block_label.as_str() == filter_label {
matches.push(QueryResult::Block(block.clone()));
}
}
}
}
matches
}
fn result_query(field: &Field, query_results: Vec<QueryResult>) -> Vec<QueryResult> {
let mut matches = Vec::new();
for query_result in query_results {
match query_result {
QueryResult::Expr(expr) => {
if let Expression::Object(object) = expr {
if let Ok(id) = Identifier::new(&field.name) {
let key = ObjectKey::Identifier(id);
if let Some(expr) = object.get(&key) {
matches.push(QueryResult::Expr(expr.clone()));
}
}
let key = ObjectKey::Expression(Expression::String(field.name.clone()));
if let Some(expr) = object.get(&key) {
matches.push(QueryResult::Expr(expr.clone()));
}
}
}
QueryResult::Block(block) => {
let mut body_matches = body_query(field, block.body());
matches.append(&mut body_matches);
}
}
}
matches
}