nu_plugin_hcl 0.90.1

A nushell plugin for parsing Hashicorp Configuration Language file format
use nu_plugin::{EvaluatedCall, LabeledError, Plugin};
use nu_protocol::{record, Category, PluginExample, PluginSignature, Span, Type, Value};
use serde_json::Value as SerdeJsonValue;

pub struct FromHcl;

impl FromHcl {
    pub fn new() -> Self {
        Self {}
    }
}

pub fn examples() -> Vec<PluginExample> {
    let span = Span::test_data();
    let vec = vec![PluginExample {
        description: "Convert .hcl data into record".into(),
        example: "'provider \"aws\" {
  region = \"us-east-1\"
}
resource \"aws_instance\" \"web\" {
  ami           = \"ami-a1b2c3d4\"
  instance_type = \"t2.micro\"
}' | from hcl"
            .into(),
        result: Some(Value::record(
            record! {
                    "provider".to_string()=>Value::record(
                        record!{
                            "aws".to_string() => Value::record
                            (    record!{
                                "region".to_string()=>Value::test_string("us-east-1")
                            },span
                            )
                        }
                    ,span
                    ),

                    "resource".to_string()=> Value::record(
                        record!{"aws_instance".to_string()=>
                        Value::record (
                            record!{
                                "web".to_string()=>
                                Value::record(
                                    record! {
                                        "ami".to_string()=>Value::test_string("ami-a1b2c3d4"),
                                        "instance_type".to_string()=>Value::test_string("t2.micro"),
                                    },
                                    span,
                                )
                            },
                            span
                        )}
                        ,
                         span,
            )
                },
            span,
        )),
    }];
    vec
}

pub fn convert_sjson_to_value(value: &SerdeJsonValue, span: Span) -> Value {
    match value {
        SerdeJsonValue::Array(array) => {
            let v: Vec<Value> = array
                .iter()
                .map(|x| convert_sjson_to_value(x, span))
                .collect();

            Value::list(v, span)
        }
        SerdeJsonValue::Bool(b) => Value::bool(*b, span),
        SerdeJsonValue::Number(f) => {
            if f.is_f64() {
                Value::float(f.as_f64().unwrap(), span)
            } else {
                Value::int(f.as_i64().unwrap(), span)
            }
        }
        SerdeJsonValue::Null => Value::nothing(span),
        SerdeJsonValue::Object(k) => {
            let mut rec = record!();
            for item in k {
                rec.push(item.0.clone(), convert_sjson_to_value(item.1, span));
            }

            Value::record(rec, span)
        }
        SerdeJsonValue::String(s) => Value::string(s.clone(), span),
    }
}

impl Plugin for FromHcl {
    fn signature(&self) -> Vec<PluginSignature> {
        vec![
            PluginSignature::build("from hcl")
                .input_output_types(vec![(Type::String, Type::Record(vec![]))])
                .usage("Parse text as .hcl and create a record")
                .plugin_examples(examples())
                .category(Category::Formats),
            PluginSignature::build("from tf")
                .input_output_types(vec![(Type::String, Type::Record(vec![]))])
                .usage("Parse text as .tf and create a record")
                .plugin_examples(examples())
                .category(Category::Formats),
        ]
    }

    fn run(
        &mut self,
        _name: &str,
        _config: &Option<Value>,
        call: &EvaluatedCall,
        input: &Value,
    ) -> Result<Value, LabeledError> {
        let span = call.head;
        let input_string = input.as_string()?;

        let parse_result: SerdeJsonValue =
            hcl::from_str(&input_string).map_err(|e| LabeledError {
                label: "Error parsing hcl".into(),
                msg: e.to_string(),
                span: Some(span),
            })?;

        Ok(convert_sjson_to_value(&parse_result, span))
    }
}