///|
/// Describes a typed configuration key.
pub struct SchemaField {
path : String
kind : String
required : Bool
default_value : String?
description : String
rules : Array[Rule]
} derive(@debug.Debug, Eq)
///|
/// A named group of related fields.
pub struct SchemaSection {
name : String
description : String
fields : Array[SchemaField]
} derive(@debug.Debug, Eq)
///|
/// A complete configuration contract.
pub struct ConfigSchema {
name : String
version : String
sections : Array[SchemaSection]
} derive(@debug.Debug, Eq)
///|
pub fn field(
path : String,
kind : String,
required? : Bool = false,
default_value? : String? = None,
description? : String = "",
rules? : Array[Rule] = [],
) -> SchemaField {
{ path, kind, required, default_value, description, rules }
}
///|
pub fn section(
name : String,
description : String,
fields : Array[SchemaField],
) -> SchemaSection {
{ name, description, fields }
}
///|
pub fn schema(
name : String,
version : String,
sections : Array[SchemaSection],
) -> ConfigSchema {
{ name, version, sections }
}
///|
/// Converts schema fields into validation rules.
pub fn ConfigSchema::rules(self : ConfigSchema) -> Array[Rule] {
let result : Array[Rule] = []
for schema_section in self.sections {
for schema_field in schema_section.fields {
if schema_field.required {
result.push(required(schema_field.path))
}
for rule in schema_field.rules {
result.push(rule)
}
}
}
result
}
///|
/// Validates a merged view against a schema.
pub fn ConfigSchema::validate(
self : ConfigSchema,
view : ConfigView,
) -> Array[Diagnostic] {
validate(view, self.rules())
}
///|
/// Returns all known field paths in declaration order.
pub fn ConfigSchema::paths(self : ConfigSchema) -> Array[String] {
let result : Array[String] = []
for schema_section in self.sections {
for schema_field in schema_section.fields {
result.push(schema_field.path)
}
}
result
}
///|
/// Finds schema metadata for one path.
pub fn ConfigSchema::find_field(
self : ConfigSchema,
path : String,
) -> SchemaField? {
for schema_section in self.sections {
for schema_field in schema_section.fields {
if schema_field.path == path {
return Some(schema_field)
}
}
}
None
}
///|
/// Reports values present in the config but unknown to the schema.
pub fn ConfigSchema::unknown_key_diagnostics(
self : ConfigSchema,
view : ConfigView,
) -> Array[Diagnostic] {
let diagnostics : Array[Diagnostic] = []
let known = self.paths()
for item in view.values {
let path = entry_path(item.section, item.key)
if !array_contains(known, path) {
diagnostics.push(
warning(
"unknown-key",
"key '" + path + "' is not declared in schema '" + self.name + "'",
span=Some(item.span),
),
)
}
}
diagnostics
}
///|
/// Produces a Markdown contract table.
pub fn ConfigSchema::render_markdown(self : ConfigSchema) -> String {
let builder = StringBuilder()
builder.write_string("# " + self.name + " Configuration Schema\n\n")
builder.write_string("Version: " + self.version + "\n\n")
for schema_section in self.sections {
builder.write_string("## " + schema_section.name + "\n\n")
if schema_section.description != "" {
builder.write_string(schema_section.description + "\n\n")
}
builder.write_string("| Path | Type | Required | Default | Description |\n")
builder.write_string("| --- | --- | --- | --- | --- |\n")
for schema_field in schema_section.fields {
builder.write_string(
"| " +
schema_field.path +
" | " +
schema_field.kind +
" | " +
bool_label(schema_field.required) +
" | " +
option_or_empty(schema_field.default_value) +
" | " +
schema_field.description +
" |\n",
)
}
builder.write_string("\n")
}
builder.to_string()
}
///|
fn bool_label(value : Bool) -> String {
if value {
"yes"
} else {
"no"
}
}
///|
fn option_or_empty(value : String?) -> String {
match value {
Some(text) => text
None => ""
}
}
///|
fn array_contains(values : Array[String], target : String) -> Bool {
for value in values {
if value == target {
return true
}
}
false
}
///|
/// A reusable schema for small web services.
pub fn web_service_schema() -> ConfigSchema {
schema("web-service", "1.0", [
section("server", "HTTP listener and runtime behavior.", [
field(
"server.host",
"string",
required=true,
description="Bind address or host name.",
),
field("server.port", "int", required=true, description="TCP port.", rules=[
int_range("server.port", Some(1), Some(65535)),
]),
field(
"server.mode",
"enum",
required=false,
default_value=Some("development"),
description="Runtime mode.",
rules=[enum_value("server.mode", ["development", "test", "production"])],
),
field(
"server.workers",
"int",
required=false,
default_value=Some("1"),
description="Worker count.",
rules=[int_range("server.workers", Some(1), Some(256))],
),
field(
"server.request_timeout_ms",
"int",
required=false,
default_value=Some("30000"),
description="Request timeout in milliseconds.",
rules=[int_range("server.request_timeout_ms", Some(1), Some(600000))],
),
]),
section("database", "Primary database connection settings.", [
field(
"database.url",
"string",
required=true,
description="Database connection URL.",
),
field(
"database.pool_min",
"int",
required=false,
default_value=Some("1"),
description="Minimum pool size.",
rules=[int_range("database.pool_min", Some(0), Some(1024))],
),
field(
"database.pool_max",
"int",
required=false,
default_value=Some("16"),
description="Maximum pool size.",
rules=[int_range("database.pool_max", Some(1), Some(4096))],
),
field(
"database.password",
"secret",
required=false,
description="Database password.",
rules=[secret_like("database.password")],
),
]),
section("auth", "Authentication and authorization settings.", [
field(
"auth.enabled",
"bool",
required=false,
default_value=Some("false"),
description="Enable authentication.",
),
field(
"auth.token",
"secret",
required=false,
description="Static service token.",
rules=[secret_like("auth.token")],
),
field("auth.oauth", "bool", required=false, description="Enable OAuth."),
field(
"auth.client_id",
"string",
required=false,
description="OAuth client id.",
rules=[requires("auth.oauth", "auth.client_id")],
),
field(
"auth.basic",
"bool",
required=false,
description="Enable basic auth.",
rules=[conflicts("auth.oauth", "auth.basic")],
),
]),
])
}
///|
/// A reusable schema for CLI tools.
pub fn cli_tool_schema() -> ConfigSchema {
schema("cli-tool", "1.0", [
section("output", "Command output behavior.", [
field(
"output.format",
"enum",
required=false,
default_value=Some("text"),
description="Output format.",
rules=[enum_value("output.format", ["text", "json", "markdown"])],
),
field(
"output.color",
"enum",
required=false,
default_value=Some("auto"),
description="Color mode.",
rules=[enum_value("output.color", ["auto", "always", "never"])],
),
field(
"output.quiet",
"bool",
required=false,
default_value=Some("false"),
description="Suppress non-error output.",
),
]),
section("runtime", "Runtime limits and cache settings.", [
field(
"runtime.threads",
"int",
required=false,
default_value=Some("1"),
description="Worker thread count.",
rules=[int_range("runtime.threads", Some(1), Some(128))],
),
field(
"runtime.cache_dir",
"path",
required=false,
description="Cache directory path.",
),
field(
"runtime.timeout_ms",
"int",
required=false,
default_value=Some("30000"),
description="Global timeout.",
rules=[int_range("runtime.timeout_ms", Some(1), Some(86400000))],
),
]),
])
}
///|
/// A reusable schema for test environments.
pub fn test_environment_schema() -> ConfigSchema {
schema("test-environment", "1.0", [
section("test", "Test selection and failure policy.", [
field(
"test.pattern",
"string",
required=false,
default_value=Some("*"),
description="Test name filter.",
),
field(
"test.retries",
"int",
required=false,
default_value=Some("0"),
description="Retry count.",
rules=[int_range("test.retries", Some(0), Some(20))],
),
field(
"test.fail_fast",
"bool",
required=false,
default_value=Some("false"),
description="Stop after the first failure.",
),
]),
section("fixture", "Fixture and sandbox options.", [
field(
"fixture.root",
"path",
required=true,
description="Fixture root directory.",
),
field(
"fixture.reset",
"bool",
required=false,
default_value=Some("true"),
description="Reset fixtures before running.",
),
field(
"fixture.seed",
"int",
required=false,
default_value=Some("1"),
description="Deterministic seed.",
rules=[int_range("fixture.seed", Some(0), None)],
),
]),
])
}