Initial upload: ConfDoc v0.1.0 - Config validation and documentation generator

This commit is contained in:
2026-01-31 07:10:14 +00:00
parent dee6eb618e
commit 83d5a08c7a

View File

@@ -0,0 +1,167 @@
from typing import Any, Dict, List, Optional
class DocGenerator:
"""Generates markdown documentation from JSON Schema."""
def __init__(self, template: Optional[str] = None):
"""Initialize doc generator with optional custom template."""
self.template = template or self._default_template()
def generate(self, schema: Dict[str, Any], title: str = "Configuration Documentation") -> str:
"""Generate markdown documentation from schema."""
sections = []
sections.append(self._format_title(title))
sections.append("")
if schema.get("$schema"):
sections.append(f"*Schema: `{schema['$schema']}`*")
sections.append("")
if schema.get("title"):
sections.append(f"## {schema['title']}")
sections.append("")
if schema.get("description"):
sections.append(f"{schema['description']}")
sections.append("")
if "properties" in schema:
sections.append(self._format_properties(schema.get("properties", {}), schema.get("required", [])))
if "definitions" in schema or "$defs" in schema:
sections.append(self._format_definitions(schema.get("definitions", {}) or schema.get("$defs", {})))
return "\n".join(sections)
def _format_title(self, title: str) -> str:
"""Format document title."""
return f"# {title}\n"
def _format_properties(self, properties: Dict[str, Any], required: List[str]) -> str:
"""Format schema properties as a markdown table."""
lines = []
lines.append("## Configuration Options")
lines.append("")
lines.append("| Property | Type | Required | Default | Description |")
lines.append("|----------|------|----------|---------|-------------|")
for prop_name, prop_info in properties.items():
prop_type = self._format_type(prop_info.get("type", "any"))
is_required = "Yes" if prop_name in required else "No"
default = self._format_default(prop_info.get("default"))
description = prop_info.get("description", "")
if prop_info.get("enum"):
choices = ", ".join(str(e) for e in prop_info["enum"])
description = f"{description} (Allowed values: {choices})"
lines.append(f"| `{prop_name}` | {prop_type} | {is_required} | {default} | {description} |")
lines.append("")
return "\n".join(lines)
def _format_definitions(self, definitions: Dict[str, Any]) -> str:
"""Format schema definitions/references."""
lines = []
lines.append("## Definitions")
lines.append("")
for name, definition in definitions.items():
lines.append(f"### `{name}`")
lines.append("")
if definition.get("description"):
lines.append(definition["description"])
lines.append("")
if "properties" in definition:
lines.append("| Property | Type | Description |")
lines.append("|----------|------|-------------|")
for prop_name, prop_info in definition.get("properties", {}).items():
prop_type = self._format_type(prop_info.get("type", "any"))
description = prop_info.get("description", "")
lines.append(f"| `{prop_name}` | {prop_type} | {description} |")
lines.append("")
return "\n".join(lines)
def _format_type(self, type_name: Any) -> str:
"""Format type name for display."""
if isinstance(type_name, list):
return ", ".join(self._format_type(t) for t in type_name)
type_map = {
"string": "string",
"integer": "integer",
"number": "number",
"boolean": "boolean",
"object": "object",
"array": "array",
"null": "null",
"any": "any",
}
return type_map.get(str(type_name), str(type_name))
def _format_default(self, default: Any) -> str:
"""Format default value for display."""
if default is None:
return "-"
elif isinstance(default, bool):
return str(default).lower()
elif isinstance(default, str):
return f"`{default}`"
else:
return str(default)
def _default_template(self) -> str:
"""Return default template string."""
return "default"
def generate_table_section(self, title: str, items: List[Dict[str, Any]], columns: List[str]) -> str:
"""Generate a markdown table section."""
if not items:
return ""
lines = []
lines.append(f"## {title}")
lines.append("")
header = "| " + " | ".join(columns) + " |"
separator = "| " + "| ".join("-" * len(col) for col in columns) + " |"
lines.append(header)
lines.append(separator)
for item in items:
row = "| " + " | ".join(str(item.get(col.lower(), "")) for col in columns) + " |"
lines.append(row)
lines.append("")
return "\n".join(lines)
def generate_examples_section(self, examples: List[Dict[str, Any]]) -> str:
"""Generate an examples section."""
lines = []
lines.append("## Examples")
lines.append("")
for i, example in enumerate(examples, 1):
lines.append(f"### Example {i}")
lines.append("")
if example.get("description"):
lines.append(f"{example['description']}")
lines.append("")
if example.get("config"):
lines.append("```json")
lines.append(str(example['config']))
lines.append("```")
lines.append("")
return "\n".join(lines)