use chrono::{Datelike, Utc}; use scraper::Html; pub mod models; /// Panic if an error happened pub fn check_errors(html: &String, loc: &str) { let no_timetable = "Aucun créneau horaire affecté"; match html { t if t.contains(no_timetable) => panic!("URL: {} • {}", loc, no_timetable), _ => (), } } /// Print a line for the table pub fn line_table( cell_length_hours: usize, cell_length: usize, number_cell: usize, pos: models::Position, skip_with: std::collections::HashMap, ) { // Left side let ls = match pos { models::Position::Top => models::TabChar::Jtl.val(), models::Position::Middle => models::TabChar::Jl.val(), models::Position::Bottom => models::TabChar::Jbl.val(), }; // Middle let ms = match pos { models::Position::Top => models::TabChar::Jtb.val(), models::Position::Middle => models::TabChar::Jm.val(), models::Position::Bottom => models::TabChar::Jtt.val(), }; // Right side let rs = match pos { models::Position::Top => models::TabChar::Jtr.val(), models::Position::Middle => models::TabChar::Jr.val(), models::Position::Bottom => models::TabChar::Jbr.val(), }; // Right side before big cell let rs_bbc = models::TabChar::Jr.val(); // Right side big cell before big cell let rsbc_bbc = models::TabChar::Bv.val(); // Right side big cell let rsbc = models::TabChar::Jl.val(); let line = models::TabChar::Bh.val().to_string().repeat(cell_length); let line_h = models::TabChar::Bh .val() .to_string() .repeat(cell_length_hours); // Hours column match skip_with.get(&0) { Some(_) => print!("\n{}{}{}", ls, line_h, rs_bbc), None => print!("\n{}{}{}", ls, line_h, ms), }; // Courses columns let range = number_cell - 1; let mut last_day = false; for i in 0..range { // Check if it's a big cell if i == range - 1 { // Friday only if let Some(text) = skip_with.get(&i) { println!("{:^cell_length$}{}", text, rsbc_bbc); last_day = true; } } else { match skip_with.get(&i) { Some(text) => match skip_with.get(&(i + 1)) { // Match check if the next cell will be big Some(_) => print!("{:^cell_length$}{}", text, rsbc_bbc), None => print!("{:^cell_length$}{}", text, rsbc), }, None => match skip_with.get(&(i + 1)) { // Match check if the next cell will be big Some(_) => print!("{}{}", line, rs_bbc), None => print!("{}{}", line, ms), }, } } } if !last_day { println!("{}{}", line, rs); } } // Split a string in half with respect of words pub fn split_half(text: &str) -> (&str, &str) { let mid = text.len() / 2; for (i, j) in (mid..text.len()).enumerate() { if text.as_bytes()[j] == b' ' { return text.split_at(mid + i); } } text.split_at(mid) } // Reduce size of string by adding etc. to it, and cutting some info pub fn etc_str(text: &str) -> String { format!("{}...", split_half(text).0.trim()) } /// Get timetable webpage pub async fn get_webpage( level: i8, semester: i8, year: &str, user_agent: &str, ) -> Result> { let url = format!("https://silice.informatique.univ-paris-diderot.fr/ufr/U{}/EDT/visualiserEmploiDuTemps.php?quoi=M{},{}", year, level, semester); // Use custom User-Agent let client = reqwest::Client::builder().user_agent(user_agent).build()?; let html = client.get(&url).send().await?.text().await?; // Panic on error crate::utils::check_errors(&html, &url); // Parse document let document = Html::parse_document(&html); Ok(document) } /// Get the current semester depending on the current date pub fn get_semester(semester: Option) -> i8 { match semester { // Force the asked semester Some(n) => n, // Find the potential semester None => { if Utc::now().month() > 6 { // From july to december 1 } else { // from january to june 2 } } } } /// Get the current year depending on the current date pub fn get_year(year: Option, semester: i8) -> String { let wanted_year = match year { // Force the asked semester Some(n) => n, // Find the potential semester None => Utc::now().year(), }; if semester == 1 { format!("{}-{}", wanted_year, wanted_year + 1) } else { format!("{}-{}", wanted_year - 1, wanted_year) } }