init
This commit is contained in:
commit
2f672ecf27
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
Cargo.lock
|
||||
/target
|
||||
/dist
|
||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "jana_sessions_webpage"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.39"
|
||||
clap = { version = "4.5.28", features = ["derive"] }
|
||||
leptos = { version = "0.7.5", features = ["csr"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
26
README.md
Normal file
26
README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Jana Sessions Webpage
|
||||
|
||||
The webpage for Jana-Sessions (unofficial name), fully written in Rust.
|
||||
|
||||
## Building
|
||||
|
||||
The project currently uses leptos, so you'll want to install trunk (`cargo install trunk`).
|
||||
|
||||
You can build the projekt with
|
||||
```bash
|
||||
trunk build --release
|
||||
```
|
||||
which will create the app in the `target/dist` folder.
|
||||
|
||||
Alternatively you can serve it locally with
|
||||
```bash
|
||||
trunk serve --open
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Just use pythons webserver and point it to the dist folder
|
||||
```bash
|
||||
python3 -m http.server 8080 --directoy target/dist
|
||||
```
|
||||
|
||||
4
Trunk.toml
Normal file
4
Trunk.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[build]
|
||||
filehash = false
|
||||
dist = "target/dist"
|
||||
public_url = "."
|
||||
5
index.html
Normal file
5
index.html
Normal file
@ -0,0 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body></body>
|
||||
</html>
|
||||
2
leptosfmt.sh
Executable file
2
leptosfmt.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
leptosfmt -t 2 -- **/*.rs
|
||||
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
||||
tab_spaces = 2
|
||||
28
src/lib.rs
Normal file
28
src/lib.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::session_date_calculator::{Day, NthWeekday};
|
||||
use chrono::Weekday;
|
||||
use clap::Parser;
|
||||
|
||||
pub mod session_date_calculator;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct StartArgs {
|
||||
/// on which days of the month there are sessions
|
||||
#[arg(long = "sessions", num_args = 1..)]
|
||||
pub session_days: Vec<NthWeekday>
|
||||
}
|
||||
|
||||
pub fn localize_day(day: &Day) -> String {
|
||||
format!(
|
||||
"{}, {}",
|
||||
match day.weekday {
|
||||
Weekday::Mon => "Montag",
|
||||
Weekday::Tue => "Dienstag",
|
||||
Weekday::Wed => "Mittwoch",
|
||||
Weekday::Thu => "Donnerstag",
|
||||
Weekday::Fri => "Freitag",
|
||||
Weekday::Sat => "Samstag",
|
||||
Weekday::Sun => "Sonntag",
|
||||
},
|
||||
day.date.format("%d.%m.%Y")
|
||||
)
|
||||
}
|
||||
9
src/main.rs
Normal file
9
src/main.rs
Normal file
@ -0,0 +1,9 @@
|
||||
mod webpage;
|
||||
|
||||
use leptos::prelude::*;
|
||||
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
mount_to_body(webpage::App);
|
||||
}
|
||||
139
src/session_date_calculator.rs
Normal file
139
src/session_date_calculator.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use chrono::{Datelike, Days, Local, NaiveDate, ParseWeekdayError, Weekday};
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct NthWeekday {
|
||||
pub n: u8,
|
||||
pub weekday: Weekday,
|
||||
}
|
||||
|
||||
impl NthWeekday {
|
||||
pub fn new(n: u8, weekday: Weekday) -> Self {
|
||||
Self { n, weekday }
|
||||
}
|
||||
|
||||
pub fn matches(&self, day: &Day) -> bool {
|
||||
self.weekday == day.weekday && self.n == day.week_of_month
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for NthWeekday {
|
||||
type Err = NthWeekdayParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (number, day) = s
|
||||
.split_once(' ')
|
||||
.ok_or(NthWeekdayParseError::InvalidFormat)?;
|
||||
let weekday = Weekday::from_str(day)
|
||||
.map_err(|e| NthWeekdayParseError::InvalidWeekday(day.to_string(), e))?;
|
||||
let number = number
|
||||
.trim_end_matches(|c: char| !c.is_ascii_digit())
|
||||
.parse::<u8>()
|
||||
.map_err(|e| NthWeekdayParseError::InvalidN(number.to_string(), e))?;
|
||||
Ok(Self::new(number, weekday))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NthWeekdayParseError {
|
||||
InvalidFormat,
|
||||
InvalidWeekday(String, ParseWeekdayError),
|
||||
InvalidN(String, ParseIntError),
|
||||
}
|
||||
|
||||
impl Display for NthWeekdayParseError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
NthWeekdayParseError::InvalidFormat => write!(f, "Invalid format, use e.g '3rd fri'"),
|
||||
NthWeekdayParseError::InvalidWeekday(week_day, e) => {
|
||||
write!(f, "Invalid weekday '{week_day}': {e}")
|
||||
}
|
||||
NthWeekdayParseError::InvalidN(number, e) => write!(f, "Invalid number '{number}': {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for NthWeekdayParseError {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DayIter {
|
||||
date: Option<NaiveDate>,
|
||||
}
|
||||
|
||||
impl From<NaiveDate> for DayIter {
|
||||
fn from(value: NaiveDate) -> Self {
|
||||
Self { date: value.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Day> for DayIter {
|
||||
fn from(value: Day) -> Self {
|
||||
Self {
|
||||
date: value.date.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DayIter {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
date: today().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for DayIter {
|
||||
type Item = Day;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let date = self.date;
|
||||
self.date = date.and_then(|date| date.checked_add_days(Days::new(1)));
|
||||
date.map(Day::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Day {
|
||||
pub date: NaiveDate,
|
||||
pub weekday: Weekday,
|
||||
pub week_of_month: u8,
|
||||
}
|
||||
|
||||
impl From<NaiveDate> for Day {
|
||||
fn from(date: NaiveDate) -> Self {
|
||||
Self {
|
||||
date,
|
||||
weekday: date.weekday(),
|
||||
week_of_month: date.day0() as u8 / 7 + 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Day {
|
||||
fn default() -> Self {
|
||||
Self::from(today())
|
||||
}
|
||||
}
|
||||
|
||||
fn today() -> NaiveDate {
|
||||
Local::now().date_naive()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::session_date_calculator::DayIter;
|
||||
use chrono::NaiveDate;
|
||||
|
||||
#[test]
|
||||
fn test_day_iter() {
|
||||
let mut day_iter = DayIter::from(NaiveDate::from_ymd_opt(2025, 2, 1).expect("valid date"));
|
||||
for week_of_month in 1..=4 {
|
||||
for _ in 0..7 {
|
||||
assert_eq!(day_iter.next().unwrap().week_of_month, week_of_month)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/webpage.rs
Normal file
11
src/webpage.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use leptos::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
let (count, set_count) = signal(0);
|
||||
|
||||
view! {
|
||||
<button on:click=move |_| { *set_count.write() += 1 }>"Click me: " {count}</button>
|
||||
<p>"Double count: " {move || count.get() * 2}</p>
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user