feat(config): Provide JSON Schema for config

This commit is contained in:
Ed Page 2025-01-02 14:41:19 -06:00
parent 752bd034d6
commit 744820fdb9
4 changed files with 459 additions and 4 deletions

76
Cargo.lock generated
View file

@ -474,6 +474,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "dyn-clone"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "edit-distance"
version = "2.1.3"
@ -623,6 +629,12 @@ dependencies = [
"walkdir",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.15.0"
@ -689,6 +701,17 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"serde",
]
[[package]]
name = "indexmap"
version = "2.7.0"
@ -696,7 +719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.15.0",
]
[[package]]
@ -1107,12 +1130,47 @@ dependencies = [
"uriparse",
]
[[package]]
name = "schemars"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
dependencies = [
"dyn-clone",
"indexmap 1.9.3",
"schemars_derive",
"semver",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.90",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.217"
@ -1153,6 +1211,17 @@ dependencies = [
"syn 2.0.90",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
]
[[package]]
name = "serde_json"
version = "1.0.134"
@ -1378,7 +1447,7 @@ version = "0.22.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
dependencies = [
"indexmap",
"indexmap 2.7.0",
"serde",
"serde_spanned",
"toml_datetime",
@ -1471,6 +1540,7 @@ dependencies = [
"maplit",
"proc-exit",
"regex",
"schemars",
"serde",
"serde-sarif",
"serde_json",
@ -1500,7 +1570,7 @@ dependencies = [
"divan",
"edit-distance",
"heck",
"indexmap",
"indexmap 2.7.0",
"itertools 0.14.0",
"phf",
"snapbox",

330
config.schema.json Normal file
View file

@ -0,0 +1,330 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Config",
"type": "object",
"properties": {
"files": {
"default": {
"extend-exclude": [],
"ignore-dot": null,
"ignore-files": null,
"ignore-global": null,
"ignore-hidden": null,
"ignore-parent": null,
"ignore-vcs": null
},
"allOf": [
{
"$ref": "#/definitions/Walk"
}
]
},
"default": {
"default": {
"binary": null,
"check-file": null,
"check-filename": null,
"extend-identifiers": {},
"extend-ignore-identifiers-re": [],
"extend-ignore-re": [],
"extend-ignore-words-re": [],
"extend-words": {},
"identifier-leading-digits": null,
"ignore-hex": null,
"locale": null,
"unicode": null
},
"allOf": [
{
"$ref": "#/definitions/EngineConfig"
}
]
},
"type": {
"default": {},
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/GlobEngineConfig"
}
}
},
"additionalProperties": false,
"definitions": {
"Walk": {
"type": "object",
"properties": {
"extend-exclude": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"ignore-hidden": {
"description": "Skip hidden files and directories.",
"default": null,
"type": [
"boolean",
"null"
]
},
"ignore-files": {
"description": "Respect ignore files.",
"default": null,
"type": [
"boolean",
"null"
]
},
"ignore-dot": {
"description": "Respect .ignore files.",
"default": null,
"type": [
"boolean",
"null"
]
},
"ignore-vcs": {
"description": "Respect ignore files in vcs directories.",
"default": null,
"type": [
"boolean",
"null"
]
},
"ignore-global": {
"description": "Respect global ignore files.",
"default": null,
"type": [
"boolean",
"null"
]
},
"ignore-parent": {
"description": "Respect ignore files in parent directories.",
"default": null,
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": false
},
"EngineConfig": {
"type": "object",
"properties": {
"binary": {
"description": "Check binary files.",
"default": null,
"type": [
"boolean",
"null"
]
},
"check-filename": {
"description": "Verifying spelling in file names.",
"default": null,
"type": [
"boolean",
"null"
]
},
"check-file": {
"description": "Verifying spelling in files.",
"default": null,
"type": [
"boolean",
"null"
]
},
"extend-ignore-re": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"unicode": {
"description": "Allow unicode characters in identifiers (and not just ASCII)",
"default": null,
"type": [
"boolean",
"null"
]
},
"ignore-hex": {
"description": "Do not check identifiers that appear to be hexadecimal values.",
"default": null,
"type": [
"boolean",
"null"
]
},
"identifier-leading-digits": {
"description": "Allow identifiers to start with digits, in addition to letters.",
"default": null,
"type": [
"boolean",
"null"
]
},
"locale": {
"default": null,
"anyOf": [
{
"$ref": "#/definitions/Locale"
},
{
"type": "null"
}
]
},
"extend-ignore-identifiers-re": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"extend-identifiers": {
"default": {},
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"extend-ignore-words-re": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"extend-words": {
"default": {},
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
},
"Locale": {
"type": "string",
"enum": [
"en",
"en-us",
"en-gb",
"en-ca",
"en-au"
]
},
"GlobEngineConfig": {
"type": "object",
"properties": {
"extend-glob": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"binary": {
"description": "Check binary files.",
"default": null,
"type": [
"boolean",
"null"
]
},
"check-filename": {
"description": "Verifying spelling in file names.",
"default": null,
"type": [
"boolean",
"null"
]
},
"check-file": {
"description": "Verifying spelling in files.",
"default": null,
"type": [
"boolean",
"null"
]
},
"extend-ignore-re": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"unicode": {
"description": "Allow unicode characters in identifiers (and not just ASCII)",
"default": null,
"type": [
"boolean",
"null"
]
},
"ignore-hex": {
"description": "Do not check identifiers that appear to be hexadecimal values.",
"default": null,
"type": [
"boolean",
"null"
]
},
"identifier-leading-digits": {
"description": "Allow identifiers to start with digits, in addition to letters.",
"default": null,
"type": [
"boolean",
"null"
]
},
"locale": {
"default": null,
"anyOf": [
{
"$ref": "#/definitions/Locale"
},
{
"type": "null"
}
]
},
"extend-ignore-identifiers-re": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"extend-identifiers": {
"default": {},
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"extend-ignore-words-re": {
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"extend-words": {
"default": {},
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}

View file

@ -33,7 +33,7 @@ pre-release-replacements = [
default = ["dict", "vars"]
dict = ["dep:typos-dict"]
vars = ["dep:typos-vars"]
unstable-schema = ["dep:schemars"]
[[bin]]
name = "typos"
@ -78,6 +78,7 @@ serde_regex = "1.1.0"
regex = "1.10.4"
encoding_rs = "0.8.34"
serde-sarif = "0.7.0"
schemars = { version = "0.8.21", features = ["preserve_order","semver"], optional = true }
[dev-dependencies]
assert_fs = "1.1"

View file

@ -1,3 +1,5 @@
#![allow(unused_qualifications)] // schemars
use std::collections::HashMap;
use kstring::KString;
@ -19,6 +21,7 @@ const PYPROJECT_TOML: &str = "pyproject.toml";
#[serde(deny_unknown_fields)]
#[serde(default)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub struct Config {
pub files: Walk,
pub default: EngineConfig,
@ -142,6 +145,7 @@ impl Config {
#[serde(deny_unknown_fields)]
#[serde(default)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub struct Walk {
pub extend_exclude: Vec<String>,
/// Skip hidden files and directories.
@ -232,7 +236,12 @@ impl Walk {
#[serde(deny_unknown_fields)]
#[serde(default)]
#[serde(transparent)]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub struct TypeEngineConfig {
#[cfg_attr(
feature = "unstable-schema",
schemars(schema_with = "hashmap_string_t::<GlobEngineConfig>")
)]
pub patterns: HashMap<KString, GlobEngineConfig>,
}
@ -301,7 +310,9 @@ impl TypeEngineConfig {
//#[serde(deny_unknown_fields)] // Doesn't work with `flatten`
#[serde(default)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub struct GlobEngineConfig {
#[cfg_attr(feature = "unstable-schema", schemars(schema_with = "vec_string"))]
pub extend_glob: Vec<KString>,
#[serde(flatten)]
pub engine: EngineConfig,
@ -318,6 +329,7 @@ impl GlobEngineConfig {
//#[serde(deny_unknown_fields)] // Doesn't work with `flatten`
#[serde(default)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub struct EngineConfig {
/// Check binary files.
pub binary: Option<bool>,
@ -330,6 +342,7 @@ pub struct EngineConfig {
#[serde(flatten)]
pub dict: DictConfig,
#[serde(with = "serde_regex")]
#[cfg_attr(feature = "unstable-schema", schemars(schema_with = "vec_string"))]
pub extend_ignore_re: Vec<regex::Regex>,
}
@ -400,6 +413,7 @@ impl Eq for EngineConfig {}
#[serde(deny_unknown_fields)]
#[serde(default)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub struct TokenizerConfig {
/// Allow unicode characters in identifiers (and not just ASCII)
pub unicode: Option<bool>,
@ -448,13 +462,24 @@ impl TokenizerConfig {
#[serde(deny_unknown_fields)]
#[serde(default)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub struct DictConfig {
pub locale: Option<Locale>,
#[serde(with = "serde_regex")]
#[cfg_attr(feature = "unstable-schema", schemars(schema_with = "vec_string"))]
pub extend_ignore_identifiers_re: Vec<regex::Regex>,
#[cfg_attr(
feature = "unstable-schema",
schemars(schema_with = "hashmap_string_string")
)]
pub extend_identifiers: HashMap<KString, KString>,
#[serde(with = "serde_regex")]
#[cfg_attr(feature = "unstable-schema", schemars(schema_with = "vec_string"))]
pub extend_ignore_words_re: Vec<regex::Regex>,
#[cfg_attr(
feature = "unstable-schema",
schemars(schema_with = "hashmap_string_string")
)]
pub extend_words: HashMap<KString, KString>,
}
@ -554,6 +579,7 @@ impl Eq for DictConfig {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
#[derive(Default)]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub enum Locale {
#[default]
En,
@ -606,10 +632,38 @@ impl std::fmt::Display for Locale {
}
}
#[cfg(feature = "unstable-schema")]
fn vec_string(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
type Type = Vec<String>;
<Type as schemars::JsonSchema>::json_schema(gen)
}
#[cfg(feature = "unstable-schema")]
fn hashmap_string_string(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
type Type = HashMap<String, String>;
<Type as schemars::JsonSchema>::json_schema(gen)
}
#[cfg(feature = "unstable-schema")]
fn hashmap_string_t<T: schemars::JsonSchema>(
gen: &mut schemars::gen::SchemaGenerator,
) -> schemars::schema::Schema {
type Type<T> = HashMap<String, T>;
<Type<T> as schemars::JsonSchema>::json_schema(gen)
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "unstable-schema")]
#[test]
fn dump_schema() {
let schema = schemars::schema_for!(Config);
let dump = serde_json::to_string_pretty(&schema).unwrap();
snapbox::assert_data_eq!(dump, snapbox::file!("../../../config.schema.json").raw());
}
#[test]
fn test_from_defaults() {
let null = Config::default();