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/expectations.gleam
import caffeine_lang/errors.{type CompilationError}
import caffeine_lang/helpers
import caffeine_lang/linker/blueprints.{type Blueprint, type BlueprintValidated}
import caffeine_lang/linker/validations
import caffeine_lang/string_distance
import caffeine_lang/value.{type Value}
import gleam/dict
import gleam/list
import gleam/result
import gleam/set
import gleam/string
/// An Expectation is a concrete implementation of an Artifact + Blueprint.
pub type Expectation {
Expectation(
name: String,
blueprint_ref: String,
inputs: dict.Dict(String, Value),
)
}
/// Validates expectations against blueprints and returns paired with their blueprints.
@internal
pub fn validate_expectations(
expectations: List(Expectation),
blueprints: List(Blueprint(BlueprintValidated)),
from source_path: String,
) -> Result(
List(#(Expectation, Blueprint(BlueprintValidated))),
CompilationError,
) {
// Validate that all blueprint_refs exist before mapping.
use _ <- result.try(validate_blueprint_refs(expectations, blueprints))
// Map expectations to blueprints since we've validated all blueprint_refs.
let expectations_blueprint_collection =
helpers.map_reference_to_referrer_over_collection(
references: blueprints,
referrers: expectations,
reference_name: fn(b) { b.name },
referrer_reference: fn(e) { e.blueprint_ref },
)
let #(org, team, service) = helpers.extract_path_prefix(source_path)
let path_prefix = org <> "." <> team <> "." <> service <> "."
// Validate that expectation inputs don't overshadow blueprint inputs.
use _ <- result.try(check_input_overshadowing(
expectations_blueprint_collection,
path_prefix,
))
// Validate that expectation.inputs provides params NOT already provided by blueprint.inputs.
use _ <- result.try(validations.validate_inputs_for_collection(
input_param_collections: expectations_blueprint_collection,
get_inputs: fn(expectation) { expectation.inputs },
get_params: fn(blueprint) {
let blueprint_input_keys = blueprint.inputs |> dict.keys
blueprint.params
|> dict.filter(fn(key, _) { !list.contains(blueprint_input_keys, key) })
},
with: fn(expectation) {
"expectation '" <> path_prefix <> expectation.name <> "'"
},
missing_inputs_ok: False,
))
// Validate unique names within a file.
use _ <- result.try(validations.validate_relevant_uniqueness(
expectations,
by: fn(e) { e.name },
label: "expectation names",
))
Ok(expectations_blueprint_collection)
}
/// Validates that every expectation's blueprint_ref matches an existing blueprint.
/// Includes Levenshtein-based "did you mean?" suggestions for unknown refs.
fn validate_blueprint_refs(
expectations: List(Expectation),
blueprints: List(Blueprint(BlueprintValidated)),
) -> Result(Nil, CompilationError) {
let blueprint_names = list.map(blueprints, fn(b) { b.name })
let blueprint_name_set = set.from_list(blueprint_names)
let missing =
expectations
|> list.filter(fn(e) { !set.contains(blueprint_name_set, e.blueprint_ref) })
|> list.map(fn(e) { e.blueprint_ref })
case missing {
[] -> Ok(Nil)
[single_ref] -> {
let suggestion =
string_distance.closest_match(single_ref, blueprint_names)
Error(errors.LinkerParseError(
msg: "Unknown blueprint reference: " <> single_ref,
context: errors.ErrorContext(..errors.empty_context(), suggestion:),
))
}
_ ->
Error(errors.linker_parse_error(
msg: "Unknown blueprint reference(s): " <> string.join(missing, ", "),
))
}
}
fn check_input_overshadowing(
expectations_blueprint_collection: List(
#(Expectation, Blueprint(BlueprintValidated)),
),
path_prefix: String,
) -> Result(Nil, CompilationError) {
validations.validate_no_overshadowing(
expectations_blueprint_collection,
get_check_collection: fn(expectation) { expectation.inputs },
get_against_collection: fn(blueprint) { blueprint.inputs },
get_error_label: fn(expectation) {
"expectation '"
<> path_prefix
<> expectation.name
<> "' - overshadowing inputs from blueprint: "
},
)
}