Trouver le jour correspondant à une date

La récupération de la date venant d'une chaîne de texte est un peu fastidieuse mais montre un élément important de la programmation Rust : La gestion des erreurs.

Dans beaucoup de langages, certaines situations lèvent une erreur et on peut être amenés à capter cette erreur avec une structure try … except …. En Rust on préfère prévoir nos fonctions pour qu'elles renvoient un type Result qui est soit Ok et dans ce cas contient la valeur désirée, soit Err pour une erreur.

Dans le cas présent, on envisage que la date fournie n'ait pas le bon format. Le retour de la fonction Date::from_str peut donc être une erreur. C'est pourquoi on envisage l'alternative : soit c'est Ok et on continue, soit c'est Err et on arrête.

panic! permet d'interrompre l'exécution en vidant proprement la mémoire.

use std::str::FromStr; // Ce trait permet d'implémenter la conversion from_str pour notre date
use std::env; // pour récupérer les arguments de la ligne de commande

const MOIS:[usize;12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const JOURS:[&str;7] = ["samedi", "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi"];
const AREF:usize = 2000; // année de référence

struct Date {
    day: usize,
    month: usize,
    year: usize,
}

impl FromStr for Date {
    type Err = std::num::ParseIntError;

    // Parse un format JJ/MM/AAAA
    fn from_str(str_date: &str) -> Result<Self, Self::Err> {
        let day: usize = usize::from_str(&str_date[0..2])?;
        let month: usize = usize::from_str(&str_date[3..5])?;
        let year: usize = usize::from_str(&str_date[6..10])?;
        Ok(Date { day, month, year })
    }
}




fn is_bissextile(annee: usize) -> bool {
    if annee % 1000 == 0 {
        true
    } else if annee % 100 == 0 {
        false
    } else if annee % 4 == 0 {
        true
    } else {
        false
    }
}

fn days_in_month(month: usize, annee:usize) -> usize {
    let n = MOIS[month - 1];
    if (month == 2) && is_bissextile(annee) {
        n + 1
    } else {
        n
    }
}

fn days_in_year(annee:usize) -> usize {
    if is_bissextile(annee) {
        366
    } else {
        365
    }
}

fn jours_finir_annee(jour: usize, mois: usize, annee:usize) -> usize {
    // renvoie le nombres de jours pour une certaines dates pour atteindre
    // le 1er janvier de l'année d'après

    let mut days:usize = days_in_month(mois, annee) - (jour-1);
    for m in (mois+1)..=12 {
        days += days_in_month(m, annee);
    }
    days
}

fn jours_depuis_debut(jour: usize, mois: usize, annee:usize) -> usize {
    // renvoie le nombres de jours pour atteindre une certaine date
    // depuis le 1e janvier de cette même année
    let mut days:usize = jour-1;
    for m in 1..mois {
        days += days_in_month(m, annee);
    }
    days
}

fn nombre_jours(jour: usize, mois: usize, annee:usize) -> usize {
    if annee >= AREF {
        let mut days = jours_depuis_debut(jour, mois, annee);
        for a in AREF..annee {
            days += days_in_year(a);
        }
        days % 7
    } else {
        let mut days = jours_finir_annee(jour, mois, annee);
        for a in (annee+1)..AREF {
            days += days_in_year(a);
        }
        7 - (days % 7)
    }
}

fn main() {
    // lecture des arguments donnés en ligne de commande
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        panic!("Entrez une date au format JJ/MM/AAAA.");
    }
    let date_str = &args[1];
    let date = match Date::from_str(date_str) {
        Ok(d) => d,
        Err(_) => panic!("Entrez une date au format JJ/MM/AAAA."),
    };
    let delta = nombre_jours(date.day, date.month, date.year);
    println!("Le {} était un {}.", date_str, JOURS[delta]);
}