1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Command-line argument parsing
use clap::{Parser, ValueEnum};
use std::path::PathBuf;
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum CalendarFormatArg {
Html,
Json,
}
#[derive(Parser, Debug, Clone)]
#[command(name = "solunatus")]
#[command(version)]
#[command(about = "High-precision astronomical CLI for sun and moon calculations", long_about = None)]
pub struct Args {
/// Latitude in decimal degrees (positive North, negative South)
#[arg(long)]
pub lat: Option<f64>,
/// Longitude in decimal degrees (positive East, negative West)
#[arg(long)]
pub lon: Option<f64>,
/// Timezone (IANA timezone name, e.g. America/New_York)
#[arg(long)]
pub tz: Option<String>,
/// Date in YYYY-MM-DD format (defaults to today)
#[arg(long)]
pub date: Option<String>,
/// Select a city from the built-in database
#[arg(long)]
pub city: Option<String>,
/// Output in JSON format
#[arg(long)]
pub json: bool,
/// Generate a calendar for the specified date range
#[arg(long)]
pub calendar: bool,
/// Calendar output format (html or json)
#[arg(long, default_value = "html", value_enum)]
pub calendar_format: CalendarFormatArg,
/// Calendar range start date (YYYY-MM-DD, supports negative years like -0999)
#[arg(long, requires = "calendar")]
pub calendar_start: Option<String>,
/// Calendar range end date (YYYY-MM-DD, supports negative years like -0999)
#[arg(long, requires = "calendar")]
pub calendar_end: Option<String>,
/// Path to write the generated calendar (stdout when omitted)
#[arg(long, requires = "calendar")]
pub calendar_output: Option<PathBuf>,
/// Force watch mode (live updates)
#[arg(long)]
pub watch: bool,
/// Disable all interactive prompts
#[arg(long)]
pub no_prompt: bool,
/// Disable saving settings to config file
#[arg(long)]
pub no_save: bool,
/// Strict mode: exit with error if events don't occur (polar regions)
#[arg(long)]
pub strict: bool,
/// Enable AI insights via a local Ollama server
#[cfg(feature = "ai-insights")]
#[arg(long)]
pub ai_insights: bool,
/// Ollama server base URL or host:port (defaults to http://localhost:11434)
#[cfg(feature = "ai-insights")]
#[arg(long, default_value = "http://localhost:11434")]
pub ai_server: String,
/// Ollama model to query for insights
#[cfg(feature = "ai-insights")]
#[arg(long, default_value = "llama3")]
pub ai_model: String,
/// Minutes between AI insight refreshes in watch mode (1-60, default 2)
#[cfg(feature = "ai-insights")]
#[arg(long, default_value_t = 2, value_parser = clap::value_parser!(u64).range(1..=60))]
pub ai_refresh_minutes: u64,
/// Generate USNO validation report comparing calculations with Naval Observatory data
#[cfg(feature = "usno-validation")]
#[arg(long)]
pub validate: bool,
}
impl Args {
pub fn should_watch(&self) -> bool {
self.watch || (!self.json && !self.no_prompt)
}
}