forked from Anri/cal8tor
Compare commits
No commits in common. "282177d1d67c86893c90238b9df129a6881c1301" and "8e18f7d2e590d0ab96380f0debf4f20e73684516" have entirely different histories.
282177d1d6
...
8e18f7d2e5
6 changed files with 259 additions and 52 deletions
53
Cargo.lock
generated
53
Cargo.lock
generated
|
@ -171,7 +171,6 @@ version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"cli-table",
|
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
"ics",
|
"ics",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -250,28 +249,6 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cli-table"
|
|
||||||
version = "0.4.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89"
|
|
||||||
dependencies = [
|
|
||||||
"cli-table-derive",
|
|
||||||
"termcolor",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cli-table-derive"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3e83a93253aaae7c74eb7428ce4faa6e219ba94886908048888701819f82fb94"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -1110,18 +1087,18 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.84"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -1559,15 +1536,6 @@ dependencies = [
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termcolor"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.49"
|
version = "1.0.49"
|
||||||
|
@ -1732,9 +1700,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.14"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
|
@ -1872,15 +1840,6 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-util"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
|
|
|
@ -20,7 +20,6 @@ ics = { version = "0.5", default-features = false }
|
||||||
uuid = { version = "1.10", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.10", features = ["v4", "fast-rng"] }
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
dialoguer = "0.11"
|
dialoguer = "0.11"
|
||||||
cli-table = { version = "0.4", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
pedantic = "warn"
|
pedantic = "warn"
|
||||||
|
|
|
@ -27,6 +27,10 @@ struct Args {
|
||||||
#[clap(short, long, value_name = "FILE NAME")]
|
#[clap(short, long, value_name = "FILE NAME")]
|
||||||
export: Option<String>,
|
export: Option<String>,
|
||||||
|
|
||||||
|
/// Size of cell of the timetable (irrelevant when exporting)
|
||||||
|
#[clap(short, long, value_name = "CELL LENGTH", default_value_t = 35)]
|
||||||
|
cl: usize,
|
||||||
|
|
||||||
/// Doesn't distinguish TD from TP
|
/// Doesn't distinguish TD from TP
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
td_are_tp: bool,
|
td_are_tp: bool,
|
||||||
|
@ -87,7 +91,7 @@ async fn main() {
|
||||||
println!("\x1b[93mNOTICE: IT WON'T WORK!!!\x1b[0m");
|
println!("\x1b[93mNOTICE: IT WON'T WORK!!!\x1b[0m");
|
||||||
// Show the calendar
|
// Show the calendar
|
||||||
println!("Affichage...");
|
println!("Affichage...");
|
||||||
timetable::display(&timetable);
|
timetable::display(&timetable, args.cl);
|
||||||
println!("Vous devrez peut-être mettre votre terminal en plein écran si ce n'est pas déjà le cas.");
|
println!("Vous devrez peut-être mettre votre terminal en plein écran si ce n'est pas déjà le cas.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
103
src/timetable.rs
103
src/timetable.rs
|
@ -6,8 +6,8 @@ use scraper::Selector;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
get_hours, get_semester, get_webpage, get_year,
|
self, get_hours, get_semester, get_webpage, get_year,
|
||||||
models::{Info, InfoList},
|
models::{Info, InfoList, Position, TabChar},
|
||||||
Capitalize,
|
Capitalize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -271,4 +271,101 @@ fn add_courses(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Display the timetable
|
/// Display the timetable
|
||||||
pub fn display(timetable: &(Arc<[String]>, (usize, Vec<models::Day>))) {}
|
pub fn display(timetable: &(Arc<[String]>, (usize, Vec<models::Day>)), cell_length: usize) {
|
||||||
|
// Cell length for hours
|
||||||
|
let clh = 11;
|
||||||
|
// Cell number
|
||||||
|
let cn = 6;
|
||||||
|
// 3/4 of cell length
|
||||||
|
let quarter = (3 * cell_length) / 4;
|
||||||
|
|
||||||
|
let sep = TabChar::Bv.val();
|
||||||
|
|
||||||
|
// Top of the tab
|
||||||
|
utils::line_table(clh, cell_length, cn, &Position::Top, &HashMap::new());
|
||||||
|
|
||||||
|
// First empty case
|
||||||
|
print!("{}{:^clh$}{}", sep, "", sep);
|
||||||
|
|
||||||
|
// Print day's of the week
|
||||||
|
let mut days = HashMap::new();
|
||||||
|
for (i, data) in timetable.1 .1.iter().enumerate() {
|
||||||
|
days.insert(i, &data.name);
|
||||||
|
print!("{:^cell_length$}{}", &data.name, sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the data of the course for utils::line_table
|
||||||
|
let mut next_skip = HashMap::new();
|
||||||
|
// For each hours -- i the hour's number
|
||||||
|
for (i, hour) in timetable.0.iter().enumerate() {
|
||||||
|
// Draw separator line
|
||||||
|
utils::line_table(clh, cell_length, cn, &Position::Middle, &next_skip);
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
next_skip = HashMap::new();
|
||||||
|
|
||||||
|
// Print hour
|
||||||
|
print!("{sep}{hour:^clh$}");
|
||||||
|
|
||||||
|
// For all the days - `j` the day's number
|
||||||
|
for (j, day) in timetable.1 .1.iter().enumerate() {
|
||||||
|
// True if we found something about the slot we are looking for
|
||||||
|
let mut info_slot = false;
|
||||||
|
|
||||||
|
// For all the courses of each days - `k` the possible course.start
|
||||||
|
for (k, course_opt) in day.courses.iter().enumerate() {
|
||||||
|
match course_opt {
|
||||||
|
// If there is a course
|
||||||
|
Some(course) => {
|
||||||
|
// Check the course's hour
|
||||||
|
if i == course.start {
|
||||||
|
// If the course uses more than one time slot
|
||||||
|
if course.size > 1 {
|
||||||
|
// If the data is too long
|
||||||
|
if course.name.len() > quarter {
|
||||||
|
let data = utils::split_half(&course.name);
|
||||||
|
next_skip.insert(j, data.1.trim());
|
||||||
|
print!("{}{:^cell_length$}", sep, data.0.trim());
|
||||||
|
} else {
|
||||||
|
next_skip.insert(j, &course.name);
|
||||||
|
print!("{}{:^cell_length$}", sep, "");
|
||||||
|
}
|
||||||
|
info_slot = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else simply print the course
|
||||||
|
// If the data is too long
|
||||||
|
if course.name.len() > quarter {
|
||||||
|
print!("{}{:^cell_length$}", sep, utils::etc_str(&course.name));
|
||||||
|
} else {
|
||||||
|
print!("{}{:^cell_length$}", sep, &course.name);
|
||||||
|
}
|
||||||
|
info_slot = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If no course was found
|
||||||
|
None => {
|
||||||
|
// Verify the "no course" is in the correct day and hour
|
||||||
|
if *days.get(&j).unwrap() == &day.name.to_string() && k == i {
|
||||||
|
// If yes print empty row because there is no course
|
||||||
|
print!("{}{:^cell_length$}", sep, "");
|
||||||
|
info_slot = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Else it was a course of another day/time
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if !info_slot {
|
||||||
|
// We found nothing about the slot because the precedent course
|
||||||
|
// takes more place than one slot
|
||||||
|
print!("{}{:^cell_length$}", sep, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print!("{sep}");
|
||||||
|
}
|
||||||
|
// Bottom of the table
|
||||||
|
utils::line_table(clh, cell_length, cn, &Position::Bottom, &HashMap::new());
|
||||||
|
}
|
||||||
|
|
96
src/utils.rs
96
src/utils.rs
|
@ -16,6 +16,102 @@ pub fn check_errors(html: &String, loc: &str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<usize, &str>,
|
||||||
|
) {
|
||||||
|
// 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!("{text:^cell_length$}{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!("{text:^cell_length$}{rsbc_bbc}"),
|
||||||
|
None => print!("{text:^cell_length$}{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
|
/// Get timetable webpage
|
||||||
pub async fn get_webpage(
|
pub async fn get_webpage(
|
||||||
level: i8,
|
level: i8,
|
||||||
|
|
|
@ -2,6 +2,58 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
||||||
|
/// Collection of char for the table
|
||||||
|
pub enum TabChar {
|
||||||
|
/// Vertical bar
|
||||||
|
Bv,
|
||||||
|
/// Horizontal bar
|
||||||
|
Bh,
|
||||||
|
/// Joint left
|
||||||
|
Jl,
|
||||||
|
/// Joint right
|
||||||
|
Jr,
|
||||||
|
/// Joint bottom left
|
||||||
|
Jbl,
|
||||||
|
/// Joint bottom right
|
||||||
|
Jbr,
|
||||||
|
/// Joint top left
|
||||||
|
Jtl,
|
||||||
|
/// Joint top right
|
||||||
|
Jtr,
|
||||||
|
/// Joint to top
|
||||||
|
Jtt,
|
||||||
|
/// Joint to bottom
|
||||||
|
Jtb,
|
||||||
|
/// Joint of the middle
|
||||||
|
Jm,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabChar {
|
||||||
|
/// Value of the element
|
||||||
|
pub fn val(&self) -> char {
|
||||||
|
match *self {
|
||||||
|
Self::Bv => '│',
|
||||||
|
Self::Bh => '─',
|
||||||
|
Self::Jl => '├',
|
||||||
|
Self::Jr => '┤',
|
||||||
|
Self::Jbl => '└',
|
||||||
|
Self::Jbr => '┘',
|
||||||
|
Self::Jtl => '┌',
|
||||||
|
Self::Jtr => '┐',
|
||||||
|
Self::Jtt => '┴',
|
||||||
|
Self::Jtb => '┬',
|
||||||
|
Self::Jm => '┼',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Position for lines inside the table
|
||||||
|
pub enum Position {
|
||||||
|
Top,
|
||||||
|
Middle,
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
|
|
||||||
pub type InfoList = Vec<(chrono::DateTime<Utc>, i64)>;
|
pub type InfoList = Vec<(chrono::DateTime<Utc>, i64)>;
|
||||||
|
|
||||||
pub struct InfoType {
|
pub struct InfoType {
|
||||||
|
|
Loading…
Reference in a new issue