use confique::Config;
use crate::spec::{FieldKindRef, SchemaRef};
pub fn doc_for<C: Config>(key: &str) -> Option<Vec<String>> {
walk(SchemaRef::from_meta(&C::META), key)
}
pub fn doc_for_runtime(schema: &crate::runtime::Schema, key: &str) -> Option<Vec<String>> {
walk(SchemaRef::from_dynamic(schema), key)
}
pub(crate) fn walk(schema: SchemaRef<'_>, dotted_key: &str) -> Option<Vec<String>> {
let segments: Vec<&str> = dotted_key.split('.').collect();
walk_segments(schema, &segments)
}
fn walk_segments(schema: SchemaRef<'_>, segments: &[&str]) -> Option<Vec<String>> {
if segments.is_empty() {
return None;
}
let head = segments[0];
for field in schema.fields() {
if segment_matches(field.name, head) {
if segments.len() == 1 {
return Some(field.doc.iter().map(|s| s.to_string()).collect());
}
return match field.kind {
FieldKindRef::Nested { schema: nested } => walk_segments(nested, &segments[1..]),
FieldKindRef::ArrayOf { schema: nested } => walk_segments(nested, &segments[1..]),
FieldKindRef::Leaf(_) => None,
};
}
}
None
}
fn segment_matches(field_name: &str, caller_segment: &str) -> bool {
if field_name == caller_segment {
return true;
}
if field_name.len() != caller_segment.len() {
return false;
}
field_name
.bytes()
.zip(caller_segment.bytes())
.all(|(f, c)| f == c || (f == b'_' && c == b'-') || (f == b'-' && c == b'_'))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fixtures::test::TestConfig;
use serde::{Deserialize, Serialize};
#[derive(confique::Config, Serialize, Deserialize, Debug)]
struct PartiallyDocumentedConfig {
#[config(default = "x")]
documented: String,
#[config(default = 0)]
undocumented: u32,
}
#[test]
fn undocumented_field_returns_some_empty_vec() {
let doc = doc_for::<PartiallyDocumentedConfig>("undocumented")
.expect("field exists, even without a doc comment");
assert!(doc.is_empty(), "expected empty doc vec, got {doc:?}");
}
#[test]
fn documented_sibling_in_same_fixture_still_returns_lines() {
let doc =
doc_for::<PartiallyDocumentedConfig>("documented").expect("documented field exists");
assert!(doc.iter().any(|line| line.contains("doc comment")));
}
#[test]
fn flat_key_returns_doc() {
let doc = doc_for::<TestConfig>("host").expect("host exists");
assert!(doc.iter().any(|line| line.contains("application host")));
}
#[test]
fn nested_key_returns_doc() {
let doc = doc_for::<TestConfig>("database.pool_size").expect("pool_size exists");
assert!(doc.iter().any(|line| line.contains("Connection pool size")));
}
#[test]
fn missing_top_level_key_returns_none() {
assert!(doc_for::<TestConfig>("nonexistent").is_none());
}
#[test]
fn missing_nested_key_returns_none() {
assert!(doc_for::<TestConfig>("database.nonexistent").is_none());
}
#[test]
fn extra_segments_past_leaf_return_none() {
assert!(doc_for::<TestConfig>("host.anything").is_none());
}
#[test]
fn empty_key_returns_none() {
assert!(doc_for::<TestConfig>("").is_none());
}
#[test]
fn kebab_spelling_finds_snake_field() {
let doc = doc_for::<TestConfig>("database.pool-size").expect("kebab spelling resolves");
assert!(doc.iter().any(|line| line.contains("Connection pool size")));
}
#[test]
fn snake_spelling_finds_snake_field() {
let doc = doc_for::<TestConfig>("database.pool_size").expect("snake spelling resolves");
assert!(doc.iter().any(|line| line.contains("Connection pool size")));
}
#[test]
fn nested_field_section_doc() {
let doc = doc_for::<TestConfig>("database").expect("section exists");
assert!(doc.iter().any(|line| line.contains("Database settings")));
}
#[test]
fn segment_matches_treats_dash_and_underscore_equivalent() {
assert!(segment_matches("pool_size", "pool-size"));
assert!(segment_matches("pool_size", "pool_size"));
assert!(segment_matches("foo-bar", "foo_bar"));
assert!(!segment_matches("pool_size", "pool"));
assert!(!segment_matches("pool_size", "pool_zize"));
}
}