Packages
caffeine_lang
4.8.3
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/linker/ir_builder.gleam
import caffeine_lang/analysis/vendor
import caffeine_lang/errors.{type CompilationError}
import caffeine_lang/helpers
import caffeine_lang/identifiers
import caffeine_lang/linker/artifacts.{type Artifact, DependencyRelations, SLO}
import caffeine_lang/linker/blueprints.{type Blueprint, type BlueprintValidated}
import caffeine_lang/linker/expectations.{type Expectation}
import caffeine_lang/linker/ir
import caffeine_lang/types.{
type AcceptedTypes, CollectionType, Defaulted, Dict, InclusiveRange,
List as ListType, ModifierType, OneOf, Optional, PrimitiveType, RecordType,
RefinementType,
}
import caffeine_lang/value
import gleam/bool
import gleam/dict
import gleam/list
import gleam/option
import gleam/result
import gleam/set.{type Set}
/// Derives the set of reserved labels from standard library artifacts.
/// Reserved labels are artifact param keys that are consumed into structured
/// fields and should not appear in misc metadata tags. The "vendor" param is
/// excluded because it is intentionally surfaced as a tag.
@internal
pub fn reserved_labels_from_artifacts(artifacts: List(Artifact)) -> Set(String) {
artifacts
|> list.flat_map(fn(artifact) { dict.keys(artifact.params) })
|> set.from_list
|> set.delete("vendor")
}
/// Build intermediate representations from validated expectations across multiple files.
@internal
pub fn build_all(
expectations_with_paths: List(
#(List(#(Expectation, Blueprint(BlueprintValidated))), String),
),
reserved_labels reserved_labels: Set(String),
) -> Result(List(ir.IntermediateRepresentation(ir.Linked)), CompilationError) {
expectations_with_paths
|> list.map(fn(pair) {
let #(expectations_blueprint_collection, file_path) = pair
build(expectations_blueprint_collection, file_path, reserved_labels)
})
|> errors.from_results()
|> result.map(list.flatten)
}
/// Build intermediate representations from validated expectations for a single file.
fn build(
expectations_blueprint_collection: List(
#(Expectation, Blueprint(BlueprintValidated)),
),
file_path: String,
reserved_labels: Set(String),
) -> Result(List(ir.IntermediateRepresentation(ir.Linked)), CompilationError) {
let #(org, team, service) = helpers.extract_path_prefix(file_path)
expectations_blueprint_collection
|> list.try_map(fn(expectation_and_blueprint_pair) {
let #(expectation, blueprint) = expectation_and_blueprint_pair
// Merge blueprint inputs with expectation inputs.
// Expectation inputs override blueprint inputs for the same key.
let merged_inputs = dict.merge(blueprint.inputs, expectation.inputs)
let value_tuples = build_value_tuples(merged_inputs, blueprint.params)
let index = helpers.index_value_tuples(value_tuples)
let misc_metadata = extract_misc_metadata(value_tuples, reserved_labels)
let unique_name = org <> "_" <> service <> "_" <> expectation.name
let artifact_data =
build_artifact_data(blueprint.artifact_refs, index)
// Resolve vendor from value tuples: required for SLO artifacts, None for dependency-only.
use resolved_vendor <- result.try(resolve_vendor_from_values(
index,
blueprint.artifact_refs,
org <> "." <> team <> "." <> service <> "." <> expectation.name,
))
Ok(ir.IntermediateRepresentation(
metadata: ir.IntermediateRepresentationMetaData(
friendly_label: identifiers.ExpectationLabel(expectation.name),
org_name: identifiers.OrgName(org),
service_name: identifiers.ServiceName(service),
blueprint_name: identifiers.BlueprintName(blueprint.name),
team_name: identifiers.TeamName(team),
misc: misc_metadata,
),
unique_identifier: unique_name,
artifact_refs: blueprint.artifact_refs,
values: value_tuples,
artifact_data: artifact_data,
vendor: resolved_vendor,
))
})
}
/// Resolves the vendor from an indexed Dict of ValueTuples at IR construction time.
/// SLO artifacts require a vendor; dependency-only artifacts get None.
fn resolve_vendor_from_values(
index: dict.Dict(String, helpers.ValueTuple),
artifact_refs: List(artifacts.ArtifactType),
identifier: String,
) -> Result(option.Option(vendor.Vendor), CompilationError) {
let has_slo = list.contains(artifact_refs, SLO)
use <- bool.guard(when: !has_slo, return: Ok(option.None))
let vendor_value = dict.get(index, "vendor")
case vendor_value {
Error(Nil) ->
Error(errors.linker_vendor_resolution_error(
msg: "expectation '" <> identifier <> "' - missing 'vendor' field",
))
Ok(vt) ->
case value.extract_string(vt.value) {
Error(_) ->
Error(errors.linker_vendor_resolution_error(
msg: "expectation '"
<> identifier
<> "' - 'vendor' field is not a string",
))
Ok(vendor_string) ->
case vendor.resolve_vendor(vendor_string) {
Ok(v) -> Ok(option.Some(v))
Error(_) ->
Error(errors.linker_vendor_resolution_error(
msg: "expectation '"
<> identifier
<> "' - unknown vendor '"
<> vendor_string
<> "'",
))
}
}
}
}
/// Build value tuples from merged inputs and params.
/// Includes both provided inputs and unprovided Optional/Defaulted params.
fn build_value_tuples(
merged_inputs: dict.Dict(String, value.Value),
params: dict.Dict(String, AcceptedTypes),
) -> List(helpers.ValueTuple) {
let provided = build_provided_value_tuples(merged_inputs, params)
let unprovided = build_unprovided_optional_value_tuples(merged_inputs, params)
list.append(provided, unprovided)
}
/// Build value tuples from provided inputs.
fn build_provided_value_tuples(
merged_inputs: dict.Dict(String, value.Value),
params: dict.Dict(String, AcceptedTypes),
) -> List(helpers.ValueTuple) {
merged_inputs
|> dict.to_list
|> list.filter_map(fn(pair) {
let #(label, val) = pair
case dict.get(params, label) {
Ok(typ) -> Ok(helpers.ValueTuple(label:, typ:, value: val))
Error(Nil) -> Error(Nil)
}
})
}
/// Build value tuples for Optional/Defaulted params that weren't provided.
/// These need to be in value_tuples so the templatizer can resolve them.
fn build_unprovided_optional_value_tuples(
merged_inputs: dict.Dict(String, value.Value),
params: dict.Dict(String, AcceptedTypes),
) -> List(helpers.ValueTuple) {
params
|> dict.to_list
|> list.filter_map(fn(param) {
let #(label, typ) = param
use <- bool.guard(
when: dict.has_key(merged_inputs, label),
return: Error(Nil),
)
case types.is_optional_or_defaulted(typ) {
True -> Ok(helpers.ValueTuple(label:, typ:, value: value.NilValue))
False -> Error(Nil)
}
})
}
/// Extract misc metadata from value tuples.
/// Filters out reserved labels (derived from artifact params) and unsupported types.
/// Each key maps to a list of string values (primitives become single-element
/// lists, collection lists are exploded, nulls are excluded).
fn extract_misc_metadata(
value_tuples: List(helpers.ValueTuple),
reserved_labels: Set(String),
) -> dict.Dict(String, List(String)) {
value_tuples
|> list.filter_map(fn(value_tuple) {
use <- bool.guard(
when: set.contains(reserved_labels, value_tuple.label),
return: Error(Nil),
)
case resolve_values_for_tag(value_tuple.typ, value_tuple.value) {
Ok([]) -> Error(Nil)
Ok(values) -> Ok(#(value_tuple.label, values))
Error(_) -> Error(Nil)
}
})
|> dict.from_list
}
/// Resolves a value tuple to a list of strings for use as tags.
/// Primitives and refinements produce a single-element list.
/// Lists are exploded into multiple string values.
/// Dicts and type alias refs are unsupported.
/// Optional(None) produces an empty list (filtered out).
/// Defaulted(None) produces the default value.
fn resolve_values_for_tag(
typ: AcceptedTypes,
val: value.Value,
) -> Result(List(String), Nil) {
case typ {
PrimitiveType(_) -> Ok([value.to_string(val)])
RefinementType(refinement) -> {
case refinement {
OneOf(inner, _) -> resolve_values_for_tag(inner, val)
InclusiveRange(inner, _, _) -> resolve_values_for_tag(inner, val)
}
}
CollectionType(ListType(inner)) -> {
case val {
value.ListValue(items) ->
items
|> list.try_map(fn(item) {
resolve_values_for_tag(inner, item)
|> result.map(fn(strings) { strings })
})
|> result.map(list.flatten)
_ -> Error(Nil)
}
}
CollectionType(Dict(_, _)) -> Error(Nil)
RecordType(_) -> Error(Nil)
ModifierType(Optional(inner)) -> {
case val {
value.NilValue -> Ok([])
_ -> resolve_values_for_tag(inner, val)
}
}
ModifierType(Defaulted(inner, default)) -> {
case val {
value.NilValue -> Ok([default])
_ -> resolve_values_for_tag(inner, val)
}
}
}
}
/// Build structured artifact data from artifact refs and an indexed Dict of ValueTuples.
fn build_artifact_data(
artifact_refs: List(artifacts.ArtifactType),
index: dict.Dict(String, helpers.ValueTuple),
) -> ir.ArtifactData {
let fields =
artifact_refs
|> list.map(fn(ref) {
case ref {
SLO -> #(SLO, ir.SloArtifactFields(build_slo_fields(index)))
DependencyRelations -> #(
DependencyRelations,
ir.DependencyArtifactFields(build_dependency_fields(index)),
)
}
})
|> dict.from_list
// Fallback: default to SLO if no artifacts matched.
let fields = case dict.is_empty(fields) {
True ->
dict.from_list([
#(SLO, ir.SloArtifactFields(build_slo_fields(index))),
])
False -> fields
}
ir.ArtifactData(fields:)
}
/// Extract SLO-specific fields from an indexed Dict of ValueTuples.
fn build_slo_fields(
index: dict.Dict(String, helpers.ValueTuple),
) -> ir.SloFields {
let threshold = helpers.extract_threshold_indexed(index)
let indicators = helpers.extract_indicators_indexed(index)
let window_in_days = helpers.extract_window_in_days_indexed(index)
let evaluation =
helpers.extract_value_indexed(index, "evaluation", value.extract_string)
|> option.from_result
let tags = helpers.extract_tags_indexed(index)
let runbook =
helpers.extract_value_indexed(index, "runbook", fn(v) {
case v {
value.NilValue -> Ok(option.None)
value.StringValue(s) -> Ok(option.Some(s))
_ -> Error(Nil)
}
})
|> result.unwrap(option.None)
ir.SloFields(
threshold: threshold,
indicators: indicators,
window_in_days: window_in_days,
evaluation: evaluation,
tags: tags,
runbook: runbook,
)
}
/// Extract dependency-specific fields from an indexed Dict of ValueTuples.
fn build_dependency_fields(
index: dict.Dict(String, helpers.ValueTuple),
) -> ir.DependencyFields {
ir.DependencyFields(
relations: helpers.extract_relations_indexed(index),
tags: helpers.extract_tags_indexed(index),
)
}