Packages
caffeine_lang
5.1.1
6.3.1
6.3.0
6.2.2
6.2.1
6.2.0
6.1.2
6.1.1
6.1.0
6.0.0
5.6.0
5.5.0
5.4.4
5.4.3
5.4.2
5.4.1
5.4.0
5.3.0
5.2.0
5.1.1
5.1.0
5.0.12
5.0.11
5.0.10
5.0.8
5.0.7
5.0.6
5.0.5
5.0.4
5.0.1
5.0.0
4.10.0
4.9.0
4.8.3
4.8.2
4.8.1
4.8.0
4.7.9
4.7.8
4.7.7
4.7.6
4.7.5
4.6.7
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.7
4.3.6
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.0.2
1.0.1
0.1.0
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.2
0.0.1
A compiler for generating reliability artifacts from service expectation definitions.
Current section
Files
Jump to
Current section
Files
src/caffeine_lang/codegen/generator_utils.gleam
import caffeine_lang/errors.{type CompilationError}
import caffeine_lang/linker/ir.{
type IntermediateRepresentation, type IntermediateRepresentationMetaData,
type SloFields, ir_to_identifier,
}
import caffeine_query_language/generator as cql_generator
import gleam/dict
import gleam/list
import gleam/option
import gleam/result
import gleam/string
import terra_madre/hcl
import terra_madre/render
import terra_madre/terraform.{
type Provider, type Resource, type TerraformSettings, type Variable,
}
/// Render a Terraform config from resources, settings, providers, and variables.
/// Assembles the standard Config structure and renders it to HCL.
@internal
pub fn render_terraform_config(
resources resources: List(Resource),
settings settings: TerraformSettings,
providers providers: List(Provider),
variables variables: List(Variable),
) -> String {
let config =
terraform.Config(
terraform: option.Some(settings),
providers: providers,
resources: resources,
data_sources: [],
variables: variables,
outputs: [],
locals: [],
modules: [],
)
render.render_config(config)
}
/// Build an HCL comment identifying the source measurement and expectation.
@internal
pub fn build_source_comment(
metadata: IntermediateRepresentationMetaData,
) -> String {
"# Caffeine: "
<> metadata.org_name.value
<> "."
<> metadata.team_name.value
<> "."
<> metadata.service_name.value
<> "."
<> metadata.friendly_label.value
<> " (measurement: "
<> metadata.measurement_name.value
<> ")"
}
/// Render a single Terraform resource to HCL string (no trailing newline).
@internal
pub fn render_resource_to_string(resource: Resource) -> String {
let config =
terraform.Config(
terraform: option.None,
providers: [],
resources: [resource],
data_sources: [],
variables: [],
outputs: [],
locals: [],
modules: [],
)
render.render_config(config)
|> string.drop_end(1)
}
/// Build a description string for an SLO resource.
/// Uses the runbook URL if present, otherwise a standard "Managed by Caffeine" message.
@internal
pub fn build_description(
ir: IntermediateRepresentation(phase),
with slo: SloFields,
) -> String {
case slo.runbook {
option.Some(url) -> "[Runbook](" <> url <> ")"
option.None ->
"Managed by Caffeine ("
<> ir.metadata.org_name.value
<> "/"
<> ir.metadata.team_name.value
<> "/"
<> ir.metadata.service_name.value
<> ")"
}
}
/// Extract evaluation expression from SLO fields, returning a codegen error if missing.
@internal
pub fn require_evaluation(
slo: SloFields,
ir: IntermediateRepresentation(phase),
vendor vendor_name: String,
) -> Result(String, CompilationError) {
slo.evaluation
|> option.to_result(resolution_error(
vendor: vendor_name,
msg: "expectation '"
<> ir_to_identifier(ir)
<> "' - missing evaluation for "
<> vendor_display_name(vendor_name)
<> " SLO",
))
}
/// Maps a vendor constant to a human-friendly display name.
fn vendor_display_name(vendor: String) -> String {
case vendor {
"datadog" -> "Datadog"
"honeycomb" -> "Honeycomb"
"dynatrace" -> "Dynatrace"
"newrelic" -> "New Relic"
other -> other
}
}
/// Resolve a CQL expression by substituting indicators, wrapping errors with vendor context.
@internal
pub fn resolve_cql_expression(
evaluation_expr: String,
indicators: dict.Dict(String, String),
ir: IntermediateRepresentation(phase),
vendor vendor_name: String,
) -> Result(String, CompilationError) {
cql_generator.resolve_slo_to_expression(evaluation_expr, indicators)
|> result.map_error(fn(err) {
resolution_error(
vendor: vendor_name,
msg: "expectation '" <> ir_to_identifier(ir) <> "' - " <> err,
)
})
}
/// Build a codegen resolution error with empty context.
@internal
pub fn resolution_error(
vendor vendor_name: String,
msg msg: String,
) -> CompilationError {
errors.generator_terraform_resolution_error(vendor: vendor_name, msg:)
}
/// Build a TerraformSettings block with a single required provider.
@internal
pub fn build_terraform_settings(
provider_name provider_name: String,
source source: String,
version version: String,
) -> TerraformSettings {
terraform.TerraformSettings(
required_version: option.None,
required_providers: dict.from_list([
#(
provider_name,
terraform.ProviderRequirement(source, option.Some(version)),
),
]),
backend: option.None,
cloud: option.None,
)
}
/// Build a Provider block with the given name and attributes.
@internal
pub fn build_provider(
name name: String,
attributes attributes: List(#(String, hcl.Expr)),
) -> Provider {
terraform.Provider(
name: name,
alias: option.None,
attributes: dict.from_list(attributes),
blocks: [],
)
}
/// Generate resources by mapping each IR to a single resource.
/// Returns an empty warnings list. Suitable for vendors without per-resource warnings.
@internal
pub fn generate_resources_simple(
irs: List(IntermediateRepresentation(phase)),
mapper mapper: fn(IntermediateRepresentation(phase)) ->
Result(Resource, CompilationError),
) -> Result(#(List(Resource), List(String)), CompilationError) {
irs
|> list.try_map(mapper)
|> result.map(fn(r) { #(r, []) })
}
/// Generate resources by mapping each IR to a list of resources, then flattening.
/// Returns an empty warnings list. Suitable for vendors that produce multiple resources per IR.
@internal
pub fn generate_resources_multi(
irs: List(IntermediateRepresentation(phase)),
mapper mapper: fn(IntermediateRepresentation(phase)) ->
Result(List(Resource), CompilationError),
) -> Result(#(List(Resource), List(String)), CompilationError) {
irs
|> list.try_map(mapper)
|> result.map(fn(lists) { #(list.flatten(lists), []) })
}