RosettaCodeData/Task/Calendar---for-REAL-program.../Rust/calendar---for-real-program...

217 lines
6.2 KiB
Rust

struct Months {
pub name: String,
pub days: i16,
pub start_wday: i16,
pub at: i16,
}
impl Months {
fn new_month(name: &str, days: i16) -> Self {
Months {
name: String::from(name),
days,
start_wday: 0,
at: 0,
}
}
}
struct Calendar {
year: i16,
width: i16,
cols: i16,
lead: i16,
gap: i16,
months: [Months; 12],
wdays: [&'static str; 7],
}
impl Calendar {
fn new_calendar(year: i16, width: i16) -> Self {
let mut calendar = Calendar {
year,
width,
cols: 0,
lead: 0,
gap: 0,
months: [
Months::new_month("JANUARY", 31),
Months::new_month("FEBRUARY", 28),
Months::new_month("MARCH", 31),
Months::new_month("APRIL", 30),
Months::new_month("MAY", 31),
Months::new_month("JUNE", 30),
Months::new_month("JULY", 31),
Months::new_month("AUGUST", 31),
Months::new_month("SEPTEMBER", 30),
Months::new_month("OCTOBER", 31),
Months::new_month("NOVEMBER", 30),
Months::new_month("DECEMBER", 31),
],
wdays: ["SU", "MO", "TU", "WE", "TH", "FR", "SA"],
};
calendar.init_months();
calendar
}
fn init_months(&mut self) {
// Check for leap year and adjust February
if (self.year % 4 == 0 && self.year % 100 != 0) || self.year % 400 == 0 {
self.months[1].days = 29;
}
// Calculate starting weekday for January
let y = self.year - 1;
let year_days = (y % 7) * (365 % 7) % 7; // (y * 365) % 7
let leap_days = ((y >> 2) - y / 100 + y / 400) % 7;
self.months[0].start_wday = (year_days + leap_days + 1) % 7;
// Calculate starting weekdays for remaining months
for i in 1..12 {
self.months[i].start_wday =
((self.months[i-1].start_wday as i16 + self.months[i-1].days as i16) % 7) as i16;
}
// Layout calculations
self.cols = (self.width + 2) / 22;
while 12 % self.cols != 0 {
self.cols -= 1;
}
self.gap = if self.cols > 1 {
let gap = (self.width - 20 * self.cols) / (self.cols - 1);
if gap > 4 { 4 } else { gap }
}
else {
0
};
// Calculate left margin to center the calendar
self.lead = (self.width - (20 + self.gap) * self.cols + self.gap + 1) >> 1;
}
// Helper method to print N spaces
fn print_spaces(&self, n: i16) {
print!("{}", " ".repeat(n as usize));
}
// Print a horizontal row of month grids
fn print_row(&mut self, row: i16) {
let from = row * self.cols;
let to = from + self.cols;
// Print month names centered
self.print_spaces(self.lead);
for c in from..to {
let name_len = self.months[c as usize].name.len() as i16;
self.print_spaces((20 - name_len) >> 1);
print!("{}", self.months[c as usize].name);
let extra_spaces = 20 - name_len - ((20 - name_len) >> 1);
let gap_spaces = if c == to - 1 { 0 } else { self.gap };
self.print_spaces(extra_spaces + gap_spaces);
}
println!();
// Print weekday headers for each month
self.print_spaces(self.lead);
for c in from..to {
for i in 0..7 {
print!("{}", self.wdays[i as usize]);
if i < 6 {
print!(" ");
}
}
if c < to - 1 {
self.print_spaces(self.gap);
} else {
println!();
}
}
// Print the calendar days for all months in this row
loop {
// Check if we've printed all days for all months in this row
let mut all_done = true;
for c in from..to {
if self.months[c as usize].at < self.months[c as usize].days {
all_done = false;
break;
}
}
if all_done {
break;
}
self.print_spaces(self.lead);
for c in from..to {
let c_usize = c as usize;
let mut i = 0;
// Print spaces for days before the 1st of the month
while i < self.months[c_usize].start_wday {
self.print_spaces(3);
i += 1;
}
// Print the days of the month
while i < 7 && self.months[c_usize].at < self.months[c_usize].days {
self.months[c_usize].at += 1;
let day = self.months[c_usize].at;
// Print day with padding
if day < 10 {
print!(" {}", day);
} else {
print!("{}", day);
}
if i < 6 || c < to - 1 {
print!(" ");
}
i += 1;
}
// Fill remaining spaces in week if needed
while i < 7 && c < to - 1 {
self.print_spaces(3);
i += 1;
}
if c < to - 1 {
self.print_spaces(self.gap - 1);
}
self.months[c_usize].start_wday = 0; // Reset for next week
}
println!();
}
println!(); // Extra line between rows of months
}
// Print the entire year's calendar
fn print_year(&mut self) {
// Print centered year heading
let year_str = self.year.to_string();
let spaces = (self.width - year_str.len() as i16) >> 1;
self.print_spaces(spaces);
println!("{}", year_str);
println!();
// Print each row of months
let rows = 12 / self.cols;
for row in 0..rows {
self.print_row(row);
}
}
}
fn main() -> Result<(), String> {
let args: Vec<String> = env::args().collect();
let mut year: i16 = 1969; // Default year
let mut width: i16 = 80; // Default width
let mut year_set = false;
// Process command line arguments
let mut i = 1;
while i < args.len() {
if args[i] == "-W" {
i += 1;
if i < args.len() {
width = args[i].parse::<i16>().map_err(|_| "Invalid width")?;
if width < 20 {
return Err("Width must be at least 20".to_string());
}
} else {
return Err("Missing width value after -W".to_string());
}
} else if !year_set {
year = match args[i].parse::<i16>() {
Ok(y) if y > 0 => y,
_ => 1969, // Default to 1969 if year is invalid
};
year_set = true;
} else {
return Err(format!("Too many arguments. Usage: {} YEAR [-W WIDTH (>= 20)]", args[0]));
}
i += 1;
}
// Generate and print the calendar
let mut calendar = Calendar::new_calendar(year, width);
calendar.print_year();
Ok(())
}