pub mod astro;
pub mod city;
pub mod config;
pub mod events;
pub mod location_source;
pub mod output;
#[cfg(feature = "ai-insights")]
pub mod ai;
pub mod benchmark;
pub mod calendar;
pub mod time_sync;
#[cfg(feature = "usno-validation")]
pub mod usno_validation;
#[doc(hidden)]
pub mod calendar_optimized;
#[doc(hidden)]
pub mod cli;
#[doc(hidden)]
pub mod cpu_features;
#[doc(hidden)]
pub mod tui;
pub use astro::{
julian_century, julian_day, normalize_degrees, normalize_degrees_signed, Location,
};
pub use city::{City, CityDatabase};
pub use config::Config;
pub use astro::moon::{
lunar_event_time, lunar_phases, lunar_position, phase_emoji, phase_name, LunarEvent,
LunarPhase, LunarPhaseType, LunarPosition,
};
pub use astro::sun::{solar_event_time, solar_noon, solar_position, SolarEvent, SolarPosition};
pub mod prelude {
pub use crate::astro::moon::{LunarEvent, LunarPhase, LunarPhaseType, LunarPosition};
pub use crate::astro::sun::{SolarEvent, SolarPosition};
pub use crate::astro::Location;
pub use crate::city::{City, CityDatabase};
pub use crate::{
batch_calculate, calculate_civil_dawn, calculate_civil_dusk, calculate_moonrise,
calculate_moonset, calculate_solar_noon, calculate_sunrise, calculate_sunset,
get_current_moon_phase, get_lunar_phases_for_month, lunar_position, solar_position,
BatchResult,
};
}
use anyhow::Result;
use chrono::{DateTime, TimeZone};
pub fn calculate_sunrise<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> Option<DateTime<Tz>> {
solar_event_time(location, date, SolarEvent::Sunrise)
}
pub fn calculate_sunset<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> Option<DateTime<Tz>> {
solar_event_time(location, date, SolarEvent::Sunset)
}
pub fn calculate_solar_noon<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> DateTime<Tz> {
solar_noon(location, date)
}
pub fn calculate_civil_dawn<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> Option<DateTime<Tz>> {
solar_event_time(location, date, SolarEvent::CivilDawn)
}
pub fn calculate_civil_dusk<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> Option<DateTime<Tz>> {
solar_event_time(location, date, SolarEvent::CivilDusk)
}
pub fn calculate_moonrise<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> Option<DateTime<Tz>> {
lunar_event_time(location, date, LunarEvent::Moonrise)
}
pub fn calculate_moonset<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> Option<DateTime<Tz>> {
lunar_event_time(location, date, LunarEvent::Moonset)
}
pub fn get_current_moon_phase<Tz: TimeZone>(
location: &Location,
date: &DateTime<Tz>,
) -> (String, String) {
let pos = lunar_position(location, date);
let name = phase_name(pos.phase_angle).to_string();
let emoji = phase_emoji(pos.phase_angle).to_string();
(name, emoji)
}
pub fn get_lunar_phases_for_month(year: i32, month: u32) -> Result<Vec<LunarPhase>> {
Ok(lunar_phases(year, month))
}
pub fn batch_calculate<Tz: TimeZone>(
location: &Location,
dates: &[DateTime<Tz>],
) -> Vec<BatchResult<Tz>>
where
Tz: Clone,
{
dates
.iter()
.map(|date| {
let sun_pos = solar_position(location, date);
let moon_pos = lunar_position(location, date);
let sunrise = calculate_sunrise(location, date);
let sunset = calculate_sunset(location, date);
let moonrise = calculate_moonrise(location, date);
let moonset = calculate_moonset(location, date);
BatchResult {
date: date.clone(),
sun_position: sun_pos,
moon_position: moon_pos,
sunrise,
sunset,
moonrise,
moonset,
}
})
.collect()
}
#[derive(Debug, Clone)]
pub struct BatchResult<Tz: TimeZone> {
pub date: DateTime<Tz>,
pub sun_position: SolarPosition,
pub moon_position: LunarPosition,
pub sunrise: Option<DateTime<Tz>>,
pub sunset: Option<DateTime<Tz>>,
pub moonrise: Option<DateTime<Tz>>,
pub moonset: Option<DateTime<Tz>>,
}
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
pub fn library_info() -> LibraryInfo {
let city_count = CityDatabase::load()
.ok()
.map(|db| db.cities().len())
.unwrap_or(0);
LibraryInfo {
version: version(),
cpu_profile: cpu_features::OptimizationProfile::current(),
city_count,
}
}
#[derive(Debug, Clone)]
pub struct LibraryInfo {
pub version: &'static str,
pub cpu_profile: cpu_features::OptimizationProfile,
pub city_count: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::Utc;
use chrono_tz::America::New_York;
#[test]
fn test_convenience_functions() {
let location = Location::new(40.7128, -74.0060).unwrap();
let date = Utc
.with_ymd_and_hms(2025, 6, 21, 12, 0, 0)
.unwrap()
.with_timezone(&New_York);
let sunrise = calculate_sunrise(&location, &date);
let sunset = calculate_sunset(&location, &date);
assert!(sunrise.is_some());
assert!(sunset.is_some());
assert!(sunrise.unwrap() < sunset.unwrap());
let solar_noon = calculate_solar_noon(&location, &date);
assert!(solar_noon > sunrise.unwrap());
assert!(solar_noon < sunset.unwrap());
}
#[test]
fn test_moon_phase() {
let location = Location::new(40.7128, -74.0060).unwrap();
let date = Utc
.with_ymd_and_hms(2025, 6, 21, 12, 0, 0)
.unwrap()
.with_timezone(&New_York);
let (name, emoji) = get_current_moon_phase(&location, &date);
assert!(!name.is_empty());
assert!(!emoji.is_empty());
}
#[test]
fn test_lunar_phases() {
let phases = get_lunar_phases_for_month(2025, 10).unwrap();
assert!(phases.len() >= 2);
assert!(phases.len() <= 5); }
#[test]
fn test_library_info() {
let info = library_info();
assert_eq!(info.version, env!("CARGO_PKG_VERSION"));
assert!(
info.city_count >= 570,
"Expected at least 570 cities, got {}",
info.city_count
);
}
}