pub(crate) const DOCS_BASE_URL: &str = "https://1nder-labs.github.io/schemalint";
const SEMANTIC_RULES: &[&str] = &[
"empty-object",
"additional-properties-object",
"anyof-objects",
];
pub(crate) fn rule_url(code: &str) -> String {
match rule_path(code) {
Some(path) => format!("{DOCS_BASE_URL}/rules/{path}"),
None => format!("{DOCS_BASE_URL}/rules"),
}
}
fn rule_path(code: &str) -> Option<String> {
let mut parts = code.splitn(3, '-');
let _prefix = parts.next()?;
let kind = parts.next()?;
let name = parts.next().filter(|n| !n.is_empty())?;
let category = match kind {
"K" if name.ends_with("-restricted") => "restriction",
"K" => "keyword",
"S" if SEMANTIC_RULES.contains(&name) => "semantic",
"S" => "structural",
_ => return None,
};
Some(format!("{category}/{name}"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn semantic_rules_list_matches_registry() {
use crate::rules::metadata::RuleCategory;
use crate::rules::registry::RULES;
use std::collections::BTreeSet;
let from_registry: BTreeSet<String> = RULES
.iter()
.filter_map(|r| r.metadata())
.filter(|m| m.category == RuleCategory::Semantic)
.map(|m| m.name)
.collect();
let from_const: BTreeSet<String> = SEMANTIC_RULES.iter().map(|s| s.to_string()).collect();
assert_eq!(
from_registry, from_const,
"SEMANTIC_RULES const in docs_url.rs is out of sync with the Semantic \
rules registered in RULES. Update SEMANTIC_RULES to match: {:?}",
from_registry
);
}
#[test]
fn keyword_code_maps_to_keyword_page() {
assert_eq!(
rule_url("OAI-K-allOf"),
format!("{DOCS_BASE_URL}/rules/keyword/allOf")
);
}
#[test]
fn restricted_code_maps_to_restriction_page() {
assert_eq!(
rule_url("OAI-K-format-restricted"),
format!("{DOCS_BASE_URL}/rules/restriction/format-restricted")
);
assert_eq!(
rule_url("ANT-K-minItems-restricted"),
format!("{DOCS_BASE_URL}/rules/restriction/minItems-restricted")
);
}
#[test]
fn structural_code_maps_to_structural_page() {
assert_eq!(
rule_url("OAI-S-all-properties-required"),
format!("{DOCS_BASE_URL}/rules/structural/all-properties-required")
);
}
#[test]
fn semantic_code_maps_to_semantic_page() {
assert_eq!(
rule_url("OAI-S-empty-object"),
format!("{DOCS_BASE_URL}/rules/semantic/empty-object")
);
assert_eq!(
rule_url("ANT-S-anyof-objects"),
format!("{DOCS_BASE_URL}/rules/semantic/anyof-objects")
);
}
#[test]
fn unrecognized_code_falls_back_to_index() {
assert_eq!(rule_url("garbage"), format!("{DOCS_BASE_URL}/rules"));
}
}