Packages

Type-safe environment variables for Gleam. Zero dependencies.

Current section

Files

Jump to
envie src envie error.gleam
Raw

src/envie/error.gleam

/// Structured error types and formatters for `envie`.
///
/// Errors include context useful for logging and user-friendly messages.
import gleam/int
import gleam/list
import gleam/string
/// An error accessing or decoding a single environment variable.
pub type Error {
/// The variable does not exist in the environment.
Missing(name: String)
/// The variable exists but its value failed validation or decoding.
InvalidValue(name: String, value: String, reason: String)
/// A generic decode failure with freeform details.
DecodeError(name: String, details: String)
}
/// An error that occurs when loading a .env file.
pub type LoadError {
/// The requested file was not found on disk.
FileNotFound(path: String)
/// The file was found but could not be read.
ReadError(path: String, reason: String)
/// The file was read but contained syntax errors.
ParseError(errors: List(ParseError))
}
/// A single syntax error found while parsing a .env file.
pub type ParseError {
/// A line that does not follow the KEY=value format.
InvalidSyntax(line: Int, content: String, reason: String)
/// An unrecognized escape sequence inside a quoted value.
InvalidEscape(line: Int, sequence: String)
}
/// Format a single `Error` into a human-readable message.
pub fn format(error: Error) -> String {
case error {
Missing(name) -> name <> ": missing required environment variable"
InvalidValue(name, value, reason) -> {
let short = case string.length(value) > 50 {
True -> string.slice(value, 0, 47) <> "..."
False -> value
}
name <> ": invalid value \"" <> short <> "\" — " <> reason
}
DecodeError(name, details) -> name <> ": decode error — " <> details
}
}
/// Format a list of `Error` values, one per line.
pub fn format_all(errors: List(Error)) -> String {
errors |> list.map(format) |> string.join("\n")
}
/// Format a `LoadError` into a human-readable message.
pub fn format_load_error(error: LoadError) -> String {
case error {
FileNotFound(path) -> "File not found: " <> path
ReadError(path, reason) -> "Failed to read " <> path <> ": " <> reason
ParseError(errors) -> "Parse errors:\n" <> format_parse_errors(errors)
}
}
/// Format a list of `ParseError` values, one per line.
pub fn format_parse_errors(errors: List(ParseError)) -> String {
errors
|> list.map(format_parse_error)
|> string.join("\n")
}
fn format_parse_error(error: ParseError) -> String {
case error {
InvalidSyntax(line, content, reason) -> {
let short = case string.length(content) > 40 {
True -> string.slice(content, 0, 37) <> "..."
False -> content
}
"Line "
<> int.to_string(line)
<> ": "
<> reason
<> " (\""
<> short
<> "\")"
}
InvalidEscape(line, sequence) ->
"Line "
<> int.to_string(line)
<> ": invalid escape sequence: "
<> sequence
}
}