This commit is contained in:
166
src/cronparse/cli.py
Normal file
166
src/cronparse/cli.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""Command-line interface for Cron Parser CLI."""
|
||||
|
||||
import sys
|
||||
|
||||
import click
|
||||
|
||||
from cronparse import __version__
|
||||
from cronparse.parser import parse_cron, validate_cron
|
||||
from cronparse.nlp import text_to_cron
|
||||
from cronparse.scheduler import get_next_executions, format_timeline
|
||||
from cronparse.generator import generate_cron_interactive
|
||||
from cronparse.describer import describe_cron
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(version=__version__, prog_name="cronparse")
|
||||
def main():
|
||||
"""Cron Parser CLI - Parse, validate, generate, and explain cron expressions."""
|
||||
pass
|
||||
|
||||
|
||||
@main.command(name="parse")
|
||||
@click.argument("expression", type=str)
|
||||
@click.option("--json", "output_json", is_flag=True, help="Output as JSON format")
|
||||
def parse_command(expression: str, output_json: bool):
|
||||
"""Parse and validate a cron expression, showing field breakdown."""
|
||||
try:
|
||||
is_valid, result = parse_cron(expression)
|
||||
if not is_valid:
|
||||
click.echo(click.style("Error: Invalid cron expression", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
if output_json:
|
||||
import json
|
||||
click.echo(json.dumps(result, indent=2))
|
||||
else:
|
||||
click.echo(click.style(f"Valid cron expression: {expression}", fg="green"))
|
||||
click.echo()
|
||||
fields = ["minute", "hour", "day", "month", "day_of_week"]
|
||||
for field in fields:
|
||||
value = result.get(field, "N/A")
|
||||
click.echo(f" {field:15}: {value}")
|
||||
if result.get("command"):
|
||||
click.echo(f" {'command':15}: {result['command']}")
|
||||
except Exception as e:
|
||||
click.echo(click.style(f"Error: {e}", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command(name="from-text")
|
||||
@click.argument("text", type=str, nargs=-1)
|
||||
@click.option("--json", "output_json", is_flag=True, help="Output as JSON format")
|
||||
def from_text_command(text: tuple, output_json: bool):
|
||||
"""Convert natural language to cron expression.
|
||||
|
||||
Examples:
|
||||
\"every Monday at 9am\"
|
||||
\"daily at 14:30\"
|
||||
\"every 5 minutes\"
|
||||
\"on the 1st of every month at midnight\"
|
||||
"""
|
||||
text_str = " ".join(text)
|
||||
try:
|
||||
result = text_to_cron(text_str)
|
||||
if output_json:
|
||||
import json
|
||||
click.echo(json.dumps(result, indent=2))
|
||||
else:
|
||||
click.echo(click.style("Natural Language:", fg="cyan") + f" {text_str}")
|
||||
click.echo(click.style("Cron Expression:", fg="green") + f" {result['cron']}")
|
||||
if result.get("description"):
|
||||
click.echo(click.style("Description:", fg="yellow") + f" {result['description']}")
|
||||
except ValueError as e:
|
||||
click.echo(click.style(f"Error: {e}", fg="red"))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
click.echo(click.style(f"Error parsing text: {e}", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command(name="next")
|
||||
@click.argument("expression", type=str)
|
||||
@click.option("--count", "-c", default=5, show_default=True, help="Number of executions to show")
|
||||
@click.option("--json", "output_json", is_flag=True, help="Output as JSON format")
|
||||
@click.option("--timeline/--no-timeline", default=True, help="Show ASCII timeline")
|
||||
def next_command(expression: str, count: int, output_json: bool, timeline: bool):
|
||||
"""Show next execution times for a cron expression."""
|
||||
try:
|
||||
is_valid, _ = validate_cron(expression)
|
||||
if not is_valid:
|
||||
click.echo(click.style("Error: Invalid cron expression", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
executions = get_next_executions(expression, count)
|
||||
if output_json:
|
||||
import json
|
||||
click.echo(json.dumps(executions, indent=2, default=str))
|
||||
else:
|
||||
click.echo(click.style(f"Next {count} executions for: {expression}", fg="cyan"))
|
||||
click.echo()
|
||||
for i, exec_time in enumerate(executions, 1):
|
||||
click.echo(f" {i}. {exec_time.strftime('%Y-%m-%d %H:%M:%S %A')}")
|
||||
|
||||
if timeline:
|
||||
click.echo()
|
||||
timeline_output = format_timeline(executions)
|
||||
click.echo(timeline_output)
|
||||
except Exception as e:
|
||||
click.echo(click.style(f"Error: {e}", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command(name="generate")
|
||||
@click.option("--json", "output_json", is_flag=True, help="Output as JSON format")
|
||||
def generate_command(output_json: bool):
|
||||
"""Interactive wizard to generate a cron expression."""
|
||||
try:
|
||||
result = generate_cron_interactive()
|
||||
if output_json:
|
||||
import json
|
||||
click.echo(json.dumps(result, indent=2))
|
||||
else:
|
||||
click.echo(click.style("Generated Cron Expression:", fg="green", bold=True))
|
||||
click.echo(f" {result['cron']}")
|
||||
click.echo()
|
||||
click.echo("Field breakdown:")
|
||||
for field, value in result["fields"].items():
|
||||
click.echo(f" {field:15}: {value}")
|
||||
except click.Abort:
|
||||
click.echo(click.style("\nGeneration cancelled.", fg="yellow"))
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
click.echo(click.style(f"Error: {e}", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@main.command(name="explain")
|
||||
@click.argument("expression", type=str)
|
||||
@click.option("--json", "output_json", is_flag=True, help="Output as JSON format")
|
||||
@click.option("--24h", "use_24h", is_flag=True, help="Use 24-hour time format")
|
||||
def explain_command(expression: str, output_json: bool, use_24h: bool):
|
||||
"""Explain a cron expression in human-readable language."""
|
||||
try:
|
||||
is_valid, _ = validate_cron(expression)
|
||||
if not is_valid:
|
||||
click.echo(click.style("Error: Invalid cron expression", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
description = describe_cron(expression, use_24h=use_24h)
|
||||
if output_json:
|
||||
import json
|
||||
click.echo(json.dumps({
|
||||
"expression": expression,
|
||||
"description": description
|
||||
}, indent=2))
|
||||
else:
|
||||
click.echo(click.style(f"Cron: {expression}", fg="cyan"))
|
||||
click.echo(click.style("Description:", fg="green"))
|
||||
click.echo(f" {description}")
|
||||
except Exception as e:
|
||||
click.echo(click.style(f"Error: {e}", fg="red"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user