Packages
caffeine_lang
3.0.6
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/middle_end/dependency_validator.gleam
import caffeine_lang/common/errors.{type CompilationError}
import caffeine_lang/middle_end/semantic_analyzer.{
type IntermediateRepresentation,
}
import gleam/dict.{type Dict}
import gleam/dynamic/decode
import gleam/list
import gleam/result
import gleam/string
/// Validates that all dependency relations reference existing expectations.
///
/// Dependencies must be in the format "org.team.service.name" and must:
/// - Reference an expectation that exists in the compilation
/// - Not reference the expectation itself (no self-references)
@internal
pub fn validate_dependency_relations(
irs: List(IntermediateRepresentation),
) -> Result(List(IntermediateRepresentation), CompilationError) {
// Build an index of all valid expectation paths
let expectation_index = build_expectation_index(irs)
// Validate each IR that has DependencyRelations
use _ <- result.try(
irs
|> list.try_each(fn(ir) {
validate_ir_dependencies(ir, expectation_index)
}),
)
Ok(irs)
}
/// Builds an index of all expectation paths for quick lookup.
/// The path format is "org.team.service.name".
@internal
pub fn build_expectation_index(
irs: List(IntermediateRepresentation),
) -> Dict(String, IntermediateRepresentation) {
irs
|> list.map(fn(ir) {
let path = ir_to_path(ir)
#(path, ir)
})
|> dict.from_list
}
fn ir_to_path(ir: IntermediateRepresentation) -> String {
ir.metadata.org_name
<> "."
<> ir.metadata.team_name
<> "."
<> ir.metadata.service_name
<> "."
<> ir.metadata.friendly_label
}
fn validate_ir_dependencies(
ir: IntermediateRepresentation,
expectation_index: Dict(String, IntermediateRepresentation),
) -> Result(Nil, CompilationError) {
// Skip IRs that don't have DependencyRelations
case ir.artifact_refs |> list.contains("DependencyRelations") {
False -> Ok(Nil)
True -> {
let self_path = ir_to_path(ir)
// Extract the relations value from the IR
let relations = extract_relations(ir)
// Get all dependency targets (from both hard and soft)
let all_targets = get_all_dependency_targets(relations)
// Validate each target
all_targets
|> list.try_each(fn(target) {
validate_dependency_target(target, self_path, expectation_index)
})
}
}
}
fn extract_relations(
ir: IntermediateRepresentation,
) -> Dict(String, List(String)) {
ir.values
|> list.filter(fn(vt) { vt.label == "relations" })
|> list.first
|> result.try(fn(vt) {
decode.run(
vt.value,
decode.dict(decode.string, decode.list(decode.string)),
)
|> result.replace_error(Nil)
})
|> result.unwrap(dict.new())
}
fn get_all_dependency_targets(relations: Dict(String, List(String))) -> List(String) {
relations
|> dict.values
|> list.flatten
}
fn validate_dependency_target(
target: String,
self_path: String,
expectation_index: Dict(String, IntermediateRepresentation),
) -> Result(Nil, CompilationError) {
// First, validate the format
case parse_dependency_path(target) {
Error(Nil) ->
Error(errors.SemanticAnalysisDependencyValidationError(
msg: "Invalid dependency reference '"
<> target
<> "' in '"
<> self_path
<> "': expected format 'org.team.service.name'",
))
Ok(_) -> {
// Check for self-reference
case target == self_path {
True ->
Error(errors.SemanticAnalysisDependencyValidationError(
msg: "Invalid dependency reference '"
<> target
<> "' in '"
<> self_path
<> "': self-reference not allowed",
))
False -> {
// Check if target exists
case dict.get(expectation_index, target) {
Ok(_) -> Ok(Nil)
Error(Nil) ->
Error(errors.SemanticAnalysisDependencyValidationError(
msg: "Invalid dependency reference '"
<> target
<> "' in '"
<> self_path
<> "': target does not exist",
))
}
}
}
}
}
}
/// Parses a dependency path into its components (org, team, service, name).
/// Returns Error if the path doesn't have exactly 4 non-empty parts.
@internal
pub fn parse_dependency_path(
path: String,
) -> Result(#(String, String, String, String), Nil) {
case string.split(path, ".") {
[org, team, service, name]
if org != "" && team != "" && service != "" && name != ""
-> Ok(#(org, team, service, name))
_ -> Error(Nil)
}
}