Packages
caffeine_lang
4.4.0
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/errors.gleam
import caffeine_lang/types.{type ValidationError}
import caffeine_lang/value.{type Value}
import gleam/list
import gleam/option.{type Option}
import gleam/string
/// Structured context attached to every compilation error.
pub type ErrorContext {
ErrorContext(
identifier: Option(String),
source_path: Option(String),
source_content: Option(String),
location: Option(SourceLocation),
suggestion: Option(String),
)
}
/// A source location within a file (1-indexed).
pub type SourceLocation {
SourceLocation(line: Int, column: Int, end_column: Option(Int))
}
/// Returns an ErrorContext with all fields set to None.
pub fn empty_context() -> ErrorContext {
ErrorContext(
identifier: option.None,
source_path: option.None,
source_content: option.None,
location: option.None,
suggestion: option.None,
)
}
/// Represents top level compilation errors.
pub type CompilationError {
// Frontend Phase (parsing .caffeine files)
FrontendParseError(msg: String, context: ErrorContext)
FrontendValidationError(msg: String, context: ErrorContext)
// Linker Phase (value and uniqueness validation)
LinkerValueValidationError(msg: String, context: ErrorContext)
LinkerDuplicateError(msg: String, context: ErrorContext)
// Linker Phase (parse step)
LinkerParseError(msg: String, context: ErrorContext)
// Linker Phase (vendor resolution)
LinkerVendorResolutionError(msg: String, context: ErrorContext)
// Semantic Analysis Phase
SemanticAnalysisTemplateParseError(msg: String, context: ErrorContext)
SemanticAnalysisTemplateResolutionError(msg: String, context: ErrorContext)
SemanticAnalysisDependencyValidationError(msg: String, context: ErrorContext)
// Code Generation Phase
GeneratorSloQueryResolutionError(msg: String, context: ErrorContext)
GeneratorTerraformResolutionError(
vendor: String,
msg: String,
context: ErrorContext,
)
// Caffeine Query Language (CQL)
CQLResolverError(msg: String, context: ErrorContext)
CQLParserError(msg: String, context: ErrorContext)
// Multiple errors accumulated from independent operations.
CompilationErrors(errors: List(CompilationError))
}
// ==== Smart constructors ====
/// Creates a FrontendParseError with empty context.
pub fn frontend_parse_error(msg msg: String) -> CompilationError {
FrontendParseError(msg:, context: empty_context())
}
/// Creates a FrontendValidationError with empty context.
pub fn frontend_validation_error(msg msg: String) -> CompilationError {
FrontendValidationError(msg:, context: empty_context())
}
/// Creates a LinkerValueValidationError with empty context.
pub fn linker_value_validation_error(msg msg: String) -> CompilationError {
LinkerValueValidationError(msg:, context: empty_context())
}
/// Creates a LinkerDuplicateError with empty context.
pub fn linker_duplicate_error(msg msg: String) -> CompilationError {
LinkerDuplicateError(msg:, context: empty_context())
}
/// Creates a LinkerParseError with empty context.
pub fn linker_parse_error(msg msg: String) -> CompilationError {
LinkerParseError(msg:, context: empty_context())
}
/// Creates a LinkerVendorResolutionError with empty context.
pub fn linker_vendor_resolution_error(msg msg: String) -> CompilationError {
LinkerVendorResolutionError(msg:, context: empty_context())
}
/// Creates a SemanticAnalysisTemplateParseError with empty context.
pub fn semantic_analysis_template_parse_error(
msg msg: String,
) -> CompilationError {
SemanticAnalysisTemplateParseError(msg:, context: empty_context())
}
/// Creates a SemanticAnalysisTemplateResolutionError with empty context.
pub fn semantic_analysis_template_resolution_error(
msg msg: String,
) -> CompilationError {
SemanticAnalysisTemplateResolutionError(msg:, context: empty_context())
}
/// Creates a SemanticAnalysisDependencyValidationError with empty context.
pub fn semantic_analysis_dependency_validation_error(
msg msg: String,
) -> CompilationError {
SemanticAnalysisDependencyValidationError(msg:, context: empty_context())
}
/// Creates a GeneratorSloQueryResolutionError with empty context.
pub fn generator_slo_query_resolution_error(msg msg: String) -> CompilationError {
GeneratorSloQueryResolutionError(msg:, context: empty_context())
}
/// Creates a GeneratorTerraformResolutionError with empty context.
pub fn generator_terraform_resolution_error(
vendor vendor: String,
msg msg: String,
) -> CompilationError {
GeneratorTerraformResolutionError(vendor:, msg:, context: empty_context())
}
/// Creates a CQLResolverError with empty context.
pub fn cql_resolver_error(msg msg: String) -> CompilationError {
CQLResolverError(msg:, context: empty_context())
}
/// Creates a CQLParserError with empty context.
pub fn cql_parser_error(msg msg: String) -> CompilationError {
CQLParserError(msg:, context: empty_context())
}
/// Extracts the ErrorContext from any CompilationError variant.
@internal
pub fn error_context(error: CompilationError) -> ErrorContext {
case error {
FrontendParseError(context:, ..) -> context
FrontendValidationError(context:, ..) -> context
LinkerValueValidationError(context:, ..) -> context
LinkerDuplicateError(context:, ..) -> context
LinkerParseError(context:, ..) -> context
LinkerVendorResolutionError(context:, ..) -> context
SemanticAnalysisTemplateParseError(context:, ..) -> context
SemanticAnalysisTemplateResolutionError(context:, ..) -> context
SemanticAnalysisDependencyValidationError(context:, ..) -> context
GeneratorSloQueryResolutionError(context:, ..) -> context
GeneratorTerraformResolutionError(context:, ..) -> context
CQLResolverError(context:, ..) -> context
CQLParserError(context:, ..) -> context
CompilationErrors(..) -> empty_context()
}
}
/// Prefixes a CompilationError's message with an identifier string.
/// Also sets the context.identifier field for structured access.
@internal
pub fn prefix_error(
error: CompilationError,
identifier: String,
) -> CompilationError {
let prefix = identifier <> " - "
case error {
FrontendParseError(msg:, context:) ->
FrontendParseError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
FrontendValidationError(msg:, context:) ->
FrontendValidationError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
LinkerValueValidationError(msg:, context:) ->
LinkerValueValidationError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
LinkerDuplicateError(msg:, context:) ->
LinkerDuplicateError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
LinkerParseError(msg:, context:) ->
LinkerParseError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
LinkerVendorResolutionError(msg:, context:) ->
LinkerVendorResolutionError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
SemanticAnalysisTemplateParseError(msg:, context:) ->
SemanticAnalysisTemplateParseError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
SemanticAnalysisTemplateResolutionError(msg:, context:) ->
SemanticAnalysisTemplateResolutionError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
SemanticAnalysisDependencyValidationError(msg:, context:) ->
SemanticAnalysisDependencyValidationError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
GeneratorSloQueryResolutionError(msg:, context:) ->
GeneratorSloQueryResolutionError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
GeneratorTerraformResolutionError(vendor:, msg:, context:) ->
GeneratorTerraformResolutionError(
vendor:,
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
CQLResolverError(msg:, context:) ->
CQLResolverError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
CQLParserError(msg:, context:) ->
CQLParserError(
msg: prefix <> msg,
context: set_context_identifier(context, identifier),
)
CompilationErrors(errors:) ->
CompilationErrors(errors: list.map(errors, prefix_error(_, identifier)))
}
}
/// Sets the identifier field on an ErrorContext, preserving existing value if already set.
fn set_context_identifier(
context: ErrorContext,
identifier: String,
) -> ErrorContext {
case context.identifier {
option.Some(_) -> context
option.None -> ErrorContext(..context, identifier: option.Some(identifier))
}
}
/// Flattens a CompilationError into a list of individual errors.
/// Single-error variants become a one-element list; CompilationErrors is unwrapped.
@internal
pub fn to_list(error: CompilationError) -> List(CompilationError) {
case error {
CompilationErrors(errors:) -> list.flat_map(errors, to_list)
_ -> [error]
}
}
/// Extracts the message from any CompilationError variant.
/// For CompilationErrors, joins all messages with newlines.
@internal
pub fn to_message(error: CompilationError) -> String {
case error {
CompilationErrors(errors:) ->
errors
|> list.flat_map(to_list)
|> list.map(to_message)
|> string.join("\n")
FrontendParseError(msg:, ..) -> msg
FrontendValidationError(msg:, ..) -> msg
LinkerValueValidationError(msg:, ..) -> msg
LinkerDuplicateError(msg:, ..) -> msg
LinkerParseError(msg:, ..) -> msg
LinkerVendorResolutionError(msg:, ..) -> msg
SemanticAnalysisTemplateParseError(msg:, ..) -> msg
SemanticAnalysisTemplateResolutionError(msg:, ..) -> msg
SemanticAnalysisDependencyValidationError(msg:, ..) -> msg
GeneratorSloQueryResolutionError(msg:, ..) -> msg
GeneratorTerraformResolutionError(msg:, ..) -> msg
CQLResolverError(msg:, ..) -> msg
CQLParserError(msg:, ..) -> msg
}
}
/// Collects results from a list of independent operations, accumulating all errors.
/// Returns Ok with all successes if none failed, or an error containing all failures.
@internal
pub fn from_results(
results: List(Result(a, CompilationError)),
) -> Result(List(a), CompilationError) {
let #(successes, failures) = partition_results(results, [], [])
case failures {
[] -> Ok(list.reverse(successes))
[single] -> Error(single)
multiple -> Error(CompilationErrors(errors: list.reverse(multiple)))
}
}
fn partition_results(
results: List(Result(a, CompilationError)),
successes: List(a),
failures: List(CompilationError),
) -> #(List(a), List(CompilationError)) {
case results {
[] -> #(successes, failures)
[Ok(val), ..rest] -> partition_results(rest, [val, ..successes], failures)
[Error(err), ..rest] ->
partition_results(rest, successes, [err, ..failures])
}
}
/// Normalizes type names across targets (JS uses "Array", Erlang uses "List").
fn normalize_type_name(name: String) -> String {
case name {
"Array" -> "List"
other -> other
}
}
/// Formats a list of validation errors into a human-readable string.
@internal
pub fn format_validation_error_message(
errors: List(ValidationError),
type_key_identifier: option.Option(String),
val: option.Option(Value),
) -> String {
let value_part = case val {
option.Some(v) -> " value (" <> value.to_preview_string(v) <> ")"
option.None -> ""
}
errors
|> list.map(fn(error) {
"expected ("
<> error.expected
<> ") received ("
<> normalize_type_name(error.found)
<> ")"
<> value_part
<> " for ("
<> {
case { error.path |> string.join(".") }, type_key_identifier {
"", option.None -> "Unknown"
path, option.None -> path
"", option.Some(val) -> val
path, option.Some(val) -> val <> "." <> path
}
}
<> ")"
})
|> string.join(", ")
}