Skip to content

Commit f9f1731

Browse files
committed
richer codegen output
1 parent f8ee8a1 commit f9f1731

2 files changed

Lines changed: 179 additions & 64 deletions

File tree

json_explorer/codegen/cli_integration.py

Lines changed: 177 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
import json
1010
from pathlib import Path
1111
from rich.console import Console
12+
from rich.table import Table
13+
from rich.panel import Panel
14+
from rich.syntax import Syntax
15+
from rich.progress import Progress, SpinnerColumn, TextColumn
16+
from rich import box
1217

1318
from . import (
1419
generate_from_analysis,
@@ -30,6 +35,10 @@ class CLIError(Exception):
3035
pass
3136

3237

38+
# Initialize rich console
39+
console = Console()
40+
41+
3342
def add_codegen_args(parser: argparse.ArgumentParser):
3443
"""Add code generation arguments to existing CLI parser."""
3544

@@ -171,10 +180,10 @@ def handle_codegen_command(args: argparse.Namespace) -> int:
171180
return _generate_and_output(json_data, language, config, args)
172181

173182
except CLIError as e:
174-
print(f"Error: {e}", file=sys.stderr)
183+
console.print(f"[red]✗ Error:[/red] {e}")
175184
return 1
176185
except Exception as e:
177-
print(f"Unexpected error: {e}", file=sys.stderr)
186+
console.print(f"[red]✗ Unexpected error:[/red] {e}")
178187
return 1
179188

180189

@@ -292,14 +301,13 @@ def _handle_codegen_subcommand(args: argparse.Namespace) -> int:
292301

293302
# Require language for generation
294303
if not args.language:
295-
print("Error: --language is required for code generation", file=sys.stderr)
304+
console.print("[red]✗[/red] --language is required for code generation")
296305
return 1
297306

298307
# Require input
299308
if not (args.file or args.url or args.stdin):
300-
print(
301-
"Error: Input source required (file, --url, or --stdin)",
302-
file=sys.stderr,
309+
console.print(
310+
"[red]✗[/red] Input source required (file, --url, or --stdin)"
303311
)
304312
return 1
305313

@@ -319,7 +327,7 @@ def _handle_codegen_subcommand(args: argparse.Namespace) -> int:
319327
return _generate_and_output(json_data, args.language, config, args)
320328

321329
except Exception as e:
322-
print(f"Error: {e}", file=sys.stderr)
330+
console.print(f"[red]✗ Error:[/red] {e}")
323331
return 1
324332

325333

@@ -329,60 +337,129 @@ def _list_languages() -> int:
329337
language_info = list_all_language_info()
330338

331339
if not language_info:
332-
print("No code generators available")
340+
console.print("[yellow]⚠️ No code generators available[/yellow]")
333341
return 0
334342

335-
print("Supported languages:")
336-
print()
343+
# Create a rich table
344+
table = Table(
345+
title="📋 Supported Languages", box=box.ROUNDED, title_style="bold cyan"
346+
)
347+
348+
table.add_column("Language", style="bold green", no_wrap=True)
349+
table.add_column("Extension", style="cyan")
350+
table.add_column("Generator Class", style="dim")
351+
table.add_column("Aliases", style="blue")
337352

338353
for lang_name, info in sorted(language_info.items()):
339-
print(f" {lang_name}")
340-
print(f" Extension: {info['file_extension']}")
341-
print(f" Class: {info['class']}")
342-
if info["aliases"]:
343-
print(f" Aliases: {', '.join(info['aliases'])}")
344-
print()
354+
aliases = (
355+
", ".join(info["aliases"]) if info["aliases"] else "[dim]none[/dim]"
356+
)
357+
358+
table.add_row(
359+
f"🔧 {lang_name}", info["file_extension"], info["class"], aliases
360+
)
361+
362+
console.print()
363+
console.print(table)
364+
console.print()
365+
366+
# Add usage hint
367+
console.print(
368+
Panel(
369+
"[bold]Usage:[/bold] json_explorer [dim]input.json[/dim] --generate [cyan]LANGUAGE[/cyan]\n"
370+
"[bold]Info:[/bold] json_explorer codegen --language-info [cyan]LANGUAGE[/cyan]",
371+
title="💡 Quick Start",
372+
border_style="blue",
373+
)
374+
)
345375

346376
return 0
347377

348378
except Exception as e:
349-
print(f"Error listing languages: {e}", file=sys.stderr)
379+
console.print(f"[red]✗ Error listing languages:[/red] {e}")
350380
return 1
351381

352382

353383
def _show_language_info(language: str) -> int:
354384
"""Show detailed information about a specific language."""
355385
try:
356386
if not _validate_language(language, silent=True):
357-
print(f"Error: Language '{language}' is not supported", file=sys.stderr)
358-
print("Use --list-languages to see available options", file=sys.stderr)
387+
console.print(f"[red]✗ Language '{language}' is not supported[/red]")
388+
console.print("[dim]Use --list-languages to see available options[/dim]")
359389
return 1
360390

361391
info = get_language_info(language)
362392

363-
print(f"Language: {info['name']}")
364-
print(f"File extension: {info['file_extension']}")
365-
print(f"Generator class: {info['class']}")
366-
print(f"Module: {info['module']}")
393+
# Create main info panel
394+
info_text = f"""[bold]Language:[/bold] {info['name']}
395+
[bold]File Extension:[/bold] {info['file_extension']}
396+
[bold]Generator Class:[/bold] {info['class']}
397+
[bold]Module:[/bold] {info['module']}"""
367398

368399
if info["aliases"]:
369-
print(f"Aliases: {', '.join(info['aliases'])}")
400+
info_text += f"\n[bold]Aliases:[/bold] {', '.join(info['aliases'])}"
401+
402+
console.print()
403+
console.print(
404+
Panel(
405+
info_text,
406+
title=f"🔧 {info['name'].title()} Generator",
407+
border_style="green",
408+
)
409+
)
370410

371-
# Try to get a sample generator to show default config
411+
# Try to get configuration details
372412
try:
373413
generator = get_generator(language)
374-
print(f"\nDefault configuration:")
375-
print(f" Package name: {generator.config.package_name}")
376-
print(f" Indent size: {generator.config.indent_size}")
377-
print(f" Generate JSON tags: {generator.config.generate_json_tags}")
378-
print(f" Add comments: {generator.config.add_comments}")
414+
415+
# Create configuration table
416+
config_table = Table(
417+
title="⚙️ Default Configuration",
418+
box=box.SIMPLE,
419+
show_header=True,
420+
header_style="bold cyan",
421+
)
422+
423+
config_table.add_column("Setting", style="bold")
424+
config_table.add_column("Value", style="green")
425+
426+
config_table.add_row("Package Name", str(generator.config.package_name))
427+
config_table.add_row("Indent Size", str(generator.config.indent_size))
428+
config_table.add_row(
429+
"Generate JSON Tags", str(generator.config.generate_json_tags)
430+
)
431+
config_table.add_row("Add Comments", str(generator.config.add_comments))
432+
config_table.add_row(
433+
"JSON Tag Omitempty", str(generator.config.json_tag_omitempty)
434+
)
435+
436+
console.print()
437+
console.print(config_table)
438+
379439
except Exception:
380-
pass
440+
console.print(
441+
"\n[yellow]⚠️ Could not retrieve configuration details[/yellow]"
442+
)
443+
444+
# Add examples panel
445+
examples_text = f"""Generate basic structure:
446+
[cyan]json_explorer codegen --language {language} data.json[/cyan]
447+
448+
Generate to file:
449+
[cyan]json_explorer codegen -l {language} -o output{info['file_extension']} data.json[/cyan]
450+
451+
Custom package name:
452+
[cyan]json_explorer codegen -l {language} --package mypackage data.json[/cyan]"""
453+
454+
console.print()
455+
console.print(
456+
Panel(examples_text, title="💡 Usage Examples", border_style="blue")
457+
)
381458

382459
return 0
383460

384461
except Exception as e:
385-
print(f"Error getting language info: {e}", file=sys.stderr)
462+
console.print(f"[red]✗ Error getting language info:[/red] {e}")
386463
return 1
387464

388465

@@ -391,8 +468,8 @@ def _validate_language(language: str, silent: bool = False) -> bool:
391468
supported = list_supported_languages()
392469
if language.lower() not in [lang.lower() for lang in supported]:
393470
if not silent:
394-
print(f"Error: Unsupported language '{language}'", file=sys.stderr)
395-
print(f"Supported languages: {', '.join(supported)}", file=sys.stderr)
471+
console.print(f"[red]✗ Unsupported language '{language}'[/red]")
472+
console.print(f"[dim]Supported languages: {', '.join(supported)}[/dim]")
396473
return False
397474
return True
398475

@@ -408,10 +485,10 @@ def _get_input_data(args: argparse.Namespace):
408485
# Try to read from stdin
409486
return json.load(sys.stdin)
410487
except json.JSONDecodeError as e:
411-
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
488+
console.print(f"[red]✗ Invalid JSON input:[/red] {e}")
412489
return None
413490
except Exception as e:
414-
print(f"Error: Failed to load input: {e}", file=sys.stderr)
491+
console.print(f"[red]✗ Failed to load input:[/red] {e}")
415492
return None
416493

417494

@@ -531,55 +608,97 @@ def _build_subcommand_config(args: argparse.Namespace) -> GeneratorConfig:
531608
def _generate_and_output(
532609
json_data, language: str, config: GeneratorConfig, args: argparse.Namespace
533610
) -> int:
534-
"""Generate code and handle output."""
611+
"""Generate code and handle output with rich formatting."""
535612
try:
536-
# Analyze JSON
537-
analysis = analyze_json(json_data)
613+
# Show analysis progress
614+
with Progress(
615+
SpinnerColumn(),
616+
TextColumn("[progress.description]{task.description}"),
617+
console=console,
618+
) as progress:
619+
620+
# Analyze JSON
621+
analyze_task = progress.add_task(
622+
"[cyan]Analyzing JSON structure...", total=None
623+
)
624+
analysis = analyze_json(json_data)
625+
progress.remove_task(analyze_task)
538626

539-
# Generate code
540-
root_name = getattr(args, "root_name", "Root")
541-
result = generate_from_analysis(analysis, language, config, root_name)
627+
# Generate code
628+
gen_task = progress.add_task(
629+
f"[green]Generating {language} code...", total=None
630+
)
631+
root_name = getattr(args, "root_name", "Root")
632+
result = generate_from_analysis(analysis, language, config, root_name)
633+
progress.remove_task(gen_task)
542634

543635
if not result.success:
544-
print(
545-
f"Error: Code generation failed: {result.error_message}",
546-
file=sys.stderr,
636+
console.print(
637+
f"[red]✗ Code generation failed:[/red] {result.error_message}"
547638
)
548639
if hasattr(result, "exception") and result.exception:
549-
print(f"Details: {result.exception}", file=sys.stderr)
640+
console.print(f"[dim]Details: {result.exception}[/dim]")
550641
return 1
551642

552-
# Show warnings
553-
if result.warnings:
554-
for warning in result.warnings:
555-
print(f"Warning: {warning}", file=sys.stderr)
556-
557643
# Output code
558644
output_file = getattr(args, "output", None)
559645
if output_file:
560646
try:
561647
output_path = Path(output_file)
562648
output_path.write_text(result.code, encoding="utf-8")
563-
print(f"Generated {language} code saved to {output_path}")
649+
console.print(
650+
f"[green]✓[/green] Generated {language} code saved to [cyan]{output_path}[/cyan]"
651+
)
564652
except IOError as e:
565-
print(f"Error: Failed to write to {output_path}: {e}", file=sys.stderr)
653+
console.print(f"[red]✗ Failed to write to {output_path}:[/red] {e}")
566654
return 1
567655
else:
568-
print(result.code)
656+
top_border = "═" * 40
657+
console.print(
658+
f"[green]{top_border} 📄 Generated {language.title()} Code {top_border}[/green]\n"
659+
)
660+
# Display code with syntax highlighting
661+
try:
662+
syntax = Syntax(result.code, language, theme="monokai")
663+
console.print(syntax)
664+
665+
except Exception:
666+
# Fallback to plain text if syntax highlighting fails
667+
console.print(result.code)
668+
console.print(f"\n[green]{top_border}{top_border}{top_border}[/green]")
569669

570670
# Show metadata if verbose
571671
if hasattr(args, "verbose") and args.verbose and result.metadata:
572-
print(f"\nGeneration metadata:", file=sys.stderr)
672+
metadata_table = Table(
673+
title="📊 Generation Metadata",
674+
box=box.SIMPLE,
675+
show_header=True,
676+
header_style="bold cyan",
677+
)
678+
679+
metadata_table.add_column("Property", style="bold")
680+
metadata_table.add_column("Value", style="green")
681+
573682
for key, value in result.metadata.items():
574-
print(f" {key}: {value}", file=sys.stderr)
683+
metadata_table.add_row(key.replace("_", " ").title(), str(value))
684+
685+
console.print()
686+
console.print(metadata_table)
687+
688+
# Show warnings with rich formatting
689+
if result.warnings:
690+
console.print("\n[yellow]⚠️ Warnings:[/yellow]")
691+
for warning in result.warnings:
692+
console.print(f" [yellow]•[/yellow] {warning}")
693+
console.print()
575694

576695
return 0
577696

578697
except GeneratorError as e:
579-
print(f"Error: {e}", file=sys.stderr)
698+
console.print(f"[red]✗[/red] {e}")
580699
return 1
581700
except Exception as e:
582-
print(f"Error: Unexpected failure: {e}", file=sys.stderr)
701+
console.print(f"[red]✗ Unexpected failure:[/red] {e}")
583702
return 1
584703

585704

json_explorer/codegen/languages/go/templates/complete_file.go.j2

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
1-
package {{ package_name }}
21

2+
package {{ package_name }}
3+
{# #}
34
{%- if imports %}
4-
55
{%- if imports|length == 1 %}
6-
76
import {{ imports[0] }}
87
{%- else %}
9-
108
import (
119
{%- for import_stmt in imports %}
1210
{{ import_stmt }}
1311
{%- endfor %}
1412
)
15-
1613
{%- endif %}
1714
{%- endif %}
1815
{# #}
1916
{%- for struct in structs %}
20-
2117
{%- if struct.description %}
2218
// {{ struct.description }}
2319
{%- endif %}

0 commit comments

Comments
 (0)