Add source code files
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-01 15:06:54 +00:00
parent e0e357d0d0
commit 27f8a69fcf

166
src/cronparse/cli.py Normal file
View 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()