Data formats

HCL

HCL, the language of Terraform, Nomad and Packer files: read as values, expressions worked out from your variables, and written back.

Version 1.0.0 · 4 functions · 1 crate

#config#terraform#devops#import

Functions

What use hcl.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use hcl_parse, hcl_read_file from hcl (Quillo 0.6).

hcl_parse(text: str) → anyor an error

HCL text as a map: attributes as keys, blocks as nested maps by their labels (resource "aws_instance" "web" { ... } is resource.aws_instance.web), a block type used several times as a list. Expressions that need evaluating (var.region, "${local.name}-web", function calls) stay as their text in ${...}.

hcl_read_file(path: str) → anyor an error

An HCL file read as hcl_parse reads text.

hcl_eval(text: str, variables: any, locals: any) → anyor an error

HCL text with its expressions worked out where they can be: var.<name> from variables (a map), local.<name> from locals, arithmetic, conditions, for expressions and template strings. What cannot be (a name not given, a type such as string, Terraform's functions) stays as its text in ${...}, as hcl_parse leaves it.

hcl_write(value: any) → stror an error

A map written as HCL text: keys holding text, numbers, booleans and lists as attributes (name = "web"), maps as maps.

Get it

$ quillo install hcl               # hcl.qwer into this folder$ quillo install hcl --example     # and its example, hcl.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/hcl.qwer$ curl -O https://quillolang.org/plugins/files/hcl.qwe$ curl -O https://quillolang.org/plugins/files/hcl_sample.tf

Then, in your program, all of it or only what you call:

main.qwe
use hcl.qweruse hcl_parse, hcl_read_file from hcl      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
hcl-rs = "0.18"

Example

hcl.qwe
// What a Terraform file deploys, read for an internal dashboard.////   quillo run hcl.qwe      then open http://localhost:8080/resourcesuse "hcl.qwer"route GET /resources {    let tf = hcl_read_file("hcl_sample.tf")?    {        region_default: tf.variable.region.default,        instance_type: tf.resource.aws_instance.web.instance_type,        kinds: keys(tf.resource),    }}route GET /evaluated {    let tf = hcl_eval(read_file("hcl_sample.tf")?, { region: "eu-central-1", size: "big" }, { name: "quillo-shop" })?    { region: tf.provider.aws.region, type: tf.resource.aws_instance.web.instance_type, name: tf.resource.aws_instance.web.tags.Name, count: tf.resource.aws_instance.web.count }}route GET /write = respond(200, hcl_write({ job: { web: { datacenters: ["ams1"], count: 3, image: "quillo/shop:1.4" } } })?, { "content-type": "text/plain" })
$ quillo run hcl.qwe$ curl 'localhost:8080/resources'$ curl 'localhost:8080/evaluated'

Source

hcl.qwer
//! HCL, the language of Terraform, Nomad, Packer and Vault files: read as values (with `var.x` left as `${var.x}`, or worked out from your variables), and values written back as HCL.//! version: 1.0.0//!//! [dependencies]//! hcl-rs = "0.18"use serde_json::Value;/// Files up to 10 MB are read.const MAX: usize = 10 * 1024 * 1024;fn size(text: &str) -> Result<(), String> {    if text.len() > MAX {        return Err("HCL files up to 10 MB".into());    }    if too_deep(text, 128) {        return Err("nested more than 128 deep".into());    }    Ok(())}/// The deepest nesting of brackets outside strings: the parser recurses, and text nested thousands deep would overflow/// the server's stack, so it is refused first.fn too_deep(text: &str, max: usize) -> bool {    let (mut depth, mut quote, mut esc) = (0usize, None::<char>, false);    for c in text.chars() {        if let Some(q) = quote {            if esc { esc = false } else if c == '\\' { esc = true } else if c == q { quote = None }            continue;        }        match c {            '"' => quote = Some(c),            '[' | '{' | '(' => {                depth += 1;                if depth > max {                    return true;                }            }            ']' | '}' | ')' => depth = depth.saturating_sub(1),            _ => {}        }    }    false}/// HCL text as a map: attributes as keys, blocks as nested maps by their labels (`resource "aws_instance" "web" { ... }` is/// `resource.aws_instance.web`), a block type used several times as a list. Expressions that need evaluating (`var.region`,/// `"${local.name}-web"`, function calls) stay as their text in `${...}`.pub fn hcl_parse(text: &str) -> Result<Value, String> {    size(text)?;    hcl::from_str::<Value>(text).map_err(|e| format!("not HCL: {}", e))}/// An HCL file read as hcl_parse reads text.pub fn hcl_read_file(path: &str) -> Result<Value, String> {    let t = std::fs::read_to_string(path).map_err(|e| format!("{}: {}", path, e))?;    hcl_parse(&t)}/// HCL text with its expressions worked out where they can be: `var.<name>` from `variables` (a map), `local.<name>` from/// `locals`, arithmetic, conditions, `for` expressions and template strings. What cannot be (a name not given, a type such as/// `string`, Terraform's functions) stays as its text in `${...}`, as hcl_parse leaves it.pub fn hcl_eval(text: &str, variables: serde_json::Value, locals: serde_json::Value) -> Result<Value, String> {    use hcl::eval::Evaluate;    size(text)?;    let mut ctx = hcl::eval::Context::new();    ctx.declare_var("var", hcl::to_value(&variables).map_err(|e| e.to_string())?);    ctx.declare_var("local", hcl::to_value(&locals).map_err(|e| e.to_string())?);    let mut body = hcl::parse(text).map_err(|e| format!("not HCL: {}", e))?;    // Every expression that can be worked out is; the errors of the others are left aside.    let _ = body.evaluate_in_place(&ctx);    hcl::from_body::<Value>(body).map_err(|e| e.to_string())}/// A map written as HCL text: keys holding text, numbers, booleans and lists as attributes (`name = "web"`), maps as maps.pub fn hcl_write(value: serde_json::Value) -> Result<String, String> {    if !value.is_object() {        return Err("HCL is written from a map".into());    }    hcl::to_string(&hcl::to_value(&value).map_err(|e| e.to_string())?).map_err(|e| e.to_string())}
Thread
76Useful? Like it, or add to its thread below.

Thread 0 posts

On HCL: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.