Skip to main content

kosher_rust/limudim/
daf_hashavua_bavli.rs

1use icu_calendar::{Date, cal::Hebrew, types::Weekday};
2
3use crate::limudim::{
4    BAVLI_DAF_COUNT_MODERN, HebrewDateExt, Limud,
5    cycle::Cycle,
6    interval::Interval,
7    limud::{CycleFinder, InternalLimud},
8    units::{BAVLI_TRACTATES, Daf, Tractate},
9};
10
11fn initial_cycle_date() -> Date<Hebrew> {
12    #[allow(clippy::expect_used)]
13    let date = Date::try_new_gregorian(2005, 3, 6)
14        .expect("hard-coded Gregorian date should be valid")
15        .to_calendar(Hebrew);
16    date
17}
18
19fn day_of_week_number(date: Date<Hebrew>) -> i32 {
20    match date.weekday() {
21        Weekday::Sunday => 1,
22        Weekday::Monday => 2,
23        Weekday::Tuesday => 3,
24        Weekday::Wednesday => 4,
25        Weekday::Thursday => 5,
26        Weekday::Friday => 6,
27        Weekday::Saturday => 7,
28    }
29}
30
31const fn start_daf(tractate: Tractate) -> u16 {
32    match tractate {
33        Tractate::Kinnim => 23,
34        Tractate::Tamid => 26,
35        Tractate::Midos => 34,
36        _ => 2,
37    }
38}
39
40const fn end_daf(tractate: Tractate) -> Option<u16> {
41    let daf = match tractate {
42        Tractate::Berachos => 64,
43        Tractate::Shabbos => 157,
44        Tractate::Eruvin => 105,
45        Tractate::Pesachim => 121,
46        Tractate::Shekalim => 22,
47        Tractate::Yoma => 88,
48        Tractate::Sukkah => 56,
49        Tractate::Beitzah => 40,
50        Tractate::RoshHashanah => 35,
51        Tractate::Taanis => 31,
52        Tractate::Megillah => 32,
53        Tractate::MoedKatan => 29,
54        Tractate::Chagigah => 27,
55        Tractate::Yevamos => 122,
56        Tractate::Kesubos => 112,
57        Tractate::Nedarim => 91,
58        Tractate::Nazir => 66,
59        Tractate::Sotah => 49,
60        Tractate::Gitin => 90,
61        Tractate::Kiddushin => 82,
62        Tractate::BavaKamma => 119,
63        Tractate::BavaMetzia => 119,
64        Tractate::BavaBasra => 176,
65        Tractate::Sanhedrin => 113,
66        Tractate::Makkos => 24,
67        Tractate::Shevuos => 49,
68        Tractate::AvodahZarah => 76,
69        Tractate::Horiyos => 14,
70        Tractate::Zevachim => 120,
71        Tractate::Menachos => 110,
72        Tractate::Chullin => 142,
73        Tractate::Bechoros => 61,
74        Tractate::Arachin => 34,
75        Tractate::Temurah => 34,
76        Tractate::Kerisos => 28,
77        Tractate::Meilah => 22,
78        Tractate::Kinnim => 25,
79        Tractate::Tamid => 33,
80        Tractate::Midos => 37,
81        Tractate::Niddah => 73,
82        _ => 0,
83    };
84    if daf == 0 { None } else { Some(daf) }
85}
86
87#[derive(Default)]
88/// Calculates the Daf Hashavua Bavli schedule.
89pub struct DafHashavuaBavli {}
90
91impl InternalLimud<Daf> for DafHashavuaBavli {
92    fn interval_end_calculation(_cycle: Cycle, hebrew_date: Date<Hebrew>) -> Option<Date<Hebrew>> {
93        let day_number = day_of_week_number(hebrew_date);
94        hebrew_date.add_days(7 - day_number)
95    }
96    fn cycle_finder(&self) -> CycleFinder {
97        CycleFinder::Initial(initial_cycle_date())
98    }
99    fn cycle_end_calculation(hebrew_date: Date<Hebrew>, _iteration: Option<i32>) -> Option<Date<Hebrew>> {
100        let day_number = day_of_week_number(hebrew_date);
101        hebrew_date.add_days((BAVLI_DAF_COUNT_MODERN * 7) - day_number)
102    }
103
104    fn unit_for_interval(&self, interval: &Interval, _limud_date: &Date<Hebrew>) -> Option<Daf> {
105        interval.cycle.iteration?;
106        daf_at_offset((interval.iteration - 1) as usize)
107    }
108}
109
110impl Limud<Daf> for DafHashavuaBavli {}
111
112fn daf_at_offset(offset: usize) -> Option<Daf> {
113    let mut remaining = offset;
114    for tractate in BAVLI_TRACTATES {
115        let start = start_daf(tractate);
116        let end = end_daf(tractate)?;
117        let count = usize::from(end - start + 1);
118        if remaining < count {
119            return Some(Daf {
120                tractate,
121                page: start + remaining as u16,
122            });
123        }
124        remaining -= count;
125    }
126    None
127}
128#[cfg(test)]
129#[allow(clippy::expect_used)]
130mod tests {
131    use crate::limudim::from_gregorian_date;
132
133    use super::*;
134
135    #[test]
136    fn daf_hashavua_bavli_simple_date() {
137        let test_date = from_gregorian_date(2018, 10, 10);
138        let limud = DafHashavuaBavli::default().limud(test_date).expect("limud exists");
139        assert_eq!(limud.tractate, Tractate::Megillah);
140        assert_eq!(limud.page, 2);
141    }
142
143    #[test]
144    fn daf_hashavua_bavli_before_cycle_began() {
145        let test_date = from_gregorian_date(2005, 3, 5);
146        let limud = DafHashavuaBavli::default().limud(test_date);
147        assert!(limud.is_none());
148    }
149
150    #[test]
151    fn daf_hashavua_bavli_first_day_of_cycle() {
152        let test_date = from_gregorian_date(2057, 2, 18);
153        let limud = DafHashavuaBavli::default().limud(test_date).expect("limud exists");
154        assert_eq!(limud.page, 2);
155        assert_eq!(limud.tractate, Tractate::Berachos);
156    }
157
158    #[test]
159    fn daf_hashavua_bavli_last_day_of_cycle() {
160        let test_date = from_gregorian_date(2057, 2, 17);
161        let limud = DafHashavuaBavli::default().limud(test_date).expect("limud exists");
162        assert_eq!(limud.page, 73);
163        assert_eq!(limud.tractate, Tractate::Niddah);
164    }
165}