Coverage for src/jsoncrack_for_sphinx/core/directive.py: 77%
74 statements
« prev ^ index » next coverage.py v7.10.0, created at 2025-07-24 22:26 +0000
« prev ^ index » next coverage.py v7.10.0, created at 2025-07-24 22:26 +0000
1"""
2Sphinx directive for manual schema inclusion.
3"""
5import re
6from pathlib import Path
7from typing import List, Optional
9from docutils import nodes
10from docutils.parsers.rst import directives
11from sphinx.util import logging
12from sphinx.util.docutils import SphinxDirective
14from ..config import get_config_values
15from ..config.config_utils import get_jsoncrack_config
16from ..generators.html_generator import generate_schema_html
18logger = logging.getLogger(__name__)
21class SchemaDirective(SphinxDirective):
22 """Directive to manually include a schema in documentation."""
24 has_content = False
25 required_arguments = 1
26 optional_arguments = 0
27 option_spec = {
28 "title": directives.unchanged,
29 "description": directives.unchanged,
30 "render_mode": directives.unchanged,
31 "theme": directives.unchanged,
32 "direction": directives.unchanged,
33 "height": directives.unchanged,
34 "width": directives.unchanged,
35 "onscreen_threshold": directives.unchanged,
36 "onscreen_margin": directives.unchanged,
37 }
39 def run(self) -> List[nodes.Node]:
40 """Process the schema directive."""
41 schema_name = self.arguments[0]
42 config = self.env.config
44 schema_path = self._find_schema_file(schema_name, config.json_schema_dir)
45 if not schema_path:
46 logger.warning(f"Schema file not found: {schema_name}")
47 return []
49 try:
50 html_content = self._generate_schema_html(schema_path)
51 return [nodes.raw("", html_content, format="html")]
52 except Exception as e:
53 logger.error(f"Error generating schema HTML: {e}")
54 return []
56 def _find_schema_file(self, schema_name: str, schema_dir: str) -> Optional[Path]:
57 """Find schema file by name."""
58 if not schema_dir:
59 return None
61 schema_dir_path = Path(schema_dir)
62 if not schema_dir_path.exists():
63 return None
65 # Try different file patterns
66 patterns = [
67 f"{schema_name}.schema.json",
68 f"{schema_name}.json",
69 ]
71 for pattern in patterns:
72 schema_path = schema_dir_path / pattern
73 if schema_path.exists():
74 return schema_path
76 return None
78 def _generate_schema_html(self, schema_path: Path) -> str:
79 """Generate HTML for JSONCrack visualization of a schema file."""
80 # Determine file type based on filename
81 file_type = (
82 "schema"
83 if schema_path.suffix == ".json" and ".schema." in schema_path.name
84 else "json"
85 )
87 # Generate HTML using the same logic as html_generator
88 html_content = generate_schema_html(schema_path, file_type, self.env.config)
90 # Apply directive options to the generated HTML
91 config = self.env.config
92 jsoncrack_config = get_jsoncrack_config(config)
93 config_values = get_config_values(jsoncrack_config)
95 # Override with directive options if provided
96 if "render_mode" in self.options:
97 config_values["render_mode"] = self.options["render_mode"]
98 if "theme" in self.options:
99 config_values["theme"] = self.options["theme"]
100 if "direction" in self.options:
101 config_values["direction"] = self.options["direction"]
102 if "height" in self.options:
103 config_values["height"] = self.options["height"]
104 if "width" in self.options:
105 config_values["width"] = self.options["width"]
106 if "onscreen_threshold" in self.options:
107 config_values["onscreen_threshold"] = self.options["onscreen_threshold"]
108 if "onscreen_margin" in self.options:
109 config_values["onscreen_margin"] = self.options["onscreen_margin"]
111 # Update data attributes in the HTML with directive options
112 for key, value in config_values.items():
113 if key in [
114 "render_mode",
115 "theme",
116 "direction",
117 "height",
118 "width",
119 "onscreen_threshold",
120 "onscreen_margin",
121 ]:
122 pattern = f'data-{key.replace("_", "-")}="[^"]*"'
123 replacement = f'data-{key.replace("_", "-")}="{value}"'
124 html_content = re.sub(pattern, replacement, html_content)
126 # Add title and description if provided
127 if "title" in self.options or "description" in self.options:
128 title = self.options.get("title", "")
129 description = self.options.get("description", "")
131 if title:
132 html_content = f"<h3>{title}</h3>" + html_content
133 if description:
134 html_content = f"<p>{description}</p>" + html_content
136 return html_content