use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SnippetConfig {
pub output: String,
#[serde(default)]
pub languages: Vec<String>,
#[serde(default)]
pub capabilities: SnippetCapabilities,
}
impl SnippetConfig {
pub fn languages_or<'a>(&'a self, fallback: &'a [String]) -> &'a [String] {
if self.languages.is_empty() {
fallback
} else {
&self.languages
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct SnippetCapabilities {
#[serde(default)]
pub all: BTreeSet<String>,
#[serde(flatten)]
pub languages: BTreeMap<String, BTreeSet<String>>,
}
impl SnippetCapabilities {
pub fn for_language(&self, language: &str) -> BTreeSet<String> {
let mut values = self.all.clone();
if let Some(language_values) = self.languages.get(language) {
values.extend(language_values.iter().cloned());
}
values
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn explicit_snippet_languages_override_e2e_targets() {
let fallback = vec!["python".to_string(), "java".to_string()];
let mut config = SnippetConfig {
output: "docs/snippets-generated".into(),
..SnippetConfig::default()
};
assert_eq!(config.languages_or(&fallback), fallback);
config.languages = vec!["python".into()];
assert_eq!(config.languages_or(&fallback), ["python"]);
}
#[test]
fn misplaced_e2e_fields_under_snippets_table_is_rejected_not_silently_dropped() {
let toml_str = r#"
output = "docs/snippets-generated"
fields_optional = ["metadata", "content"]
"#;
let err = toml::from_str::<SnippetConfig>(toml_str).expect_err(
"a SnippetConfig table carrying e2e-level field-classification keys must be \
rejected, not silently accepted with those keys discarded",
);
let message = err.to_string();
assert!(
message.contains("fields_optional"),
"error must name the offending key so the misplacement is discoverable: {message}"
);
assert!(
message.contains("unknown field"),
"error must be serde's unknown-field diagnostic, not an unrelated parse failure: {message}"
);
}
#[test]
fn well_formed_snippet_config_still_deserializes_under_deny_unknown_fields() {
let toml_str = r#"
output = "docs/snippets-generated"
languages = ["python", "java"]
[capabilities]
all = ["run"]
python = ["compile"]
"#;
let config: SnippetConfig = toml::from_str(toml_str).expect("well-formed config must still parse");
assert_eq!(config.output, "docs/snippets-generated");
assert_eq!(config.languages, vec!["python", "java"]);
assert_eq!(config.capabilities.for_language("python"), {
let mut expected = BTreeSet::new();
expected.insert("run".to_string());
expected.insert("compile".to_string());
expected
});
}
}