1#![doc(html_playground_url = "https://play.rust-lang.org")]
43#![doc(
44 html_favicon_url = "https://raw.githubusercontent.com/veeso/buongiornissimo-rs/main/docs/images/cargo/buongiornissimo-rs-128.png"
45)]
46#![doc(
47 html_logo_url = "https://raw.githubusercontent.com/veeso/buongiornissimo-rs/main/docs/images/cargo/buongiornissimo-rs-512.png"
48)]
49
50#[macro_use]
51extern crate tracing;
52
53use async_trait::async_trait;
54use chrono::NaiveDate;
55use thiserror::Error;
56use url::Url;
57
58#[cfg(feature = "moveable-feasts")]
60pub mod moveable_feasts;
61mod providers;
62
63pub use providers::{Augurando, BuongiornissimoCaffe, BuongiornoImmagini, TiCondivido};
65
66#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
68pub enum Greeting {
69 BuonGiorno,
70 BuonGiornoWeekday(chrono::Weekday),
72 Weekend,
73 BuonPomeriggio,
74 BuonPranzo,
75 BuonaNotte,
76 BuonaSerata,
77 BuonaCena,
78 Compleanno,
79 Capodanno,
81 Epifania,
82 SanValentino,
83 GiovediGrasso,
84 MartediGrasso,
85 MercolediCeneri,
86 FestaDelleDonne,
87 FestaDelPapa,
88 FestaDellaMamma,
89 DomenicaDellePalme,
90 Pasqua,
91 Pasquetta,
92 Liberazione,
93 FestaDeiLavoratori,
94 Ascensione,
95 Pentecoste,
96 DueGiugno,
97 SantissimaTrinita,
98 FestaDellaRepubblica,
99 SacroCuoreDiGesu,
100 CuoreImmacolatoDiMaria,
101 CorpusDomini,
102 Ferragosto,
103 Halloween,
104 Ognissanti,
106 Defunti,
108 SanNicola,
110 SantAmbrogio,
112 ImmacolataConcenzione,
114 SantaLucia,
116 VigiliaDiNatale,
117 Natale,
118 SantoStefano,
119 SanSilvestro,
121}
122
123pub type ScrapeResult<T> = Result<T, ScrapeError>;
125
126#[derive(Debug, Error, Eq, PartialEq)]
128pub enum ScrapeError {
129 #[error("this scraper doesn't support this greeting type")]
130 UnsupportedGreeting,
131 #[error("http error: {0}")]
132 Http(String),
133 #[error("css parser error: {0}")]
134 Css(String),
135 #[error("unexpected HTML: {0}")]
136 UnexpectedHtml(String),
137 #[error("could not find any image in the page")]
138 NoImages,
139}
140
141impl From<reqwest::Error> for ScrapeError {
142 fn from(e: reqwest::Error) -> Self {
143 Self::Http(e.to_string())
144 }
145}
146
147#[async_trait]
149pub trait Scrape {
150 async fn scrape(&self, greeting: Greeting) -> ScrapeResult<Vec<Url>>;
153}
154
155pub fn greeting_of_the_day(date: NaiveDate, use_weekday: bool) -> Greeting {
160 use chrono::Datelike;
161 match date {
162 date if date.month() == 1 && date.day() == 1 => Greeting::Capodanno,
163 date if date.month() == 1 && date.day() == 6 => Greeting::Epifania,
164 date if date.month() == 2 && date.day() == 14 => Greeting::SanValentino,
165 date if date.month() == 3 && date.day() == 8 => Greeting::FestaDelleDonne,
166 #[cfg(feature = "moveable-feasts")]
167 date if date == moveable_feasts::giovedi_grasso_date(date.year()) => {
168 Greeting::GiovediGrasso
169 }
170 #[cfg(feature = "moveable-feasts")]
171 date if date == moveable_feasts::martedi_grasso_date(date.year()) => {
172 Greeting::MartediGrasso
173 }
174 #[cfg(feature = "moveable-feasts")]
175 date if date == moveable_feasts::mercoled_ceneri_date(date.year()) => {
176 Greeting::MercolediCeneri
177 }
178 #[cfg(feature = "moveable-feasts")]
179 date if date == moveable_feasts::domenica_delle_palme_date(date.year()) => {
180 Greeting::DomenicaDellePalme
181 }
182 #[cfg(feature = "moveable-feasts")]
183 date if date == moveable_feasts::festa_della_mamma(date.year()) => {
184 Greeting::FestaDellaMamma
185 }
186 #[cfg(feature = "moveable-feasts")]
187 date if date == moveable_feasts::easter_date(date.year()) => Greeting::Pasqua,
188 #[cfg(feature = "moveable-feasts")]
189 date if date == moveable_feasts::pasquetta_date(date.year()) => Greeting::Pasquetta,
190 #[cfg(feature = "moveable-feasts")]
191 date if date == moveable_feasts::ascensione_date(date.year()) => Greeting::Ascensione,
192 #[cfg(feature = "moveable-feasts")]
193 date if date == moveable_feasts::pentecoste_date(date.year()) => Greeting::Pentecoste,
194 #[cfg(feature = "moveable-feasts")]
195 date if date == moveable_feasts::santissima_trinita_date(date.year()) => {
196 Greeting::SantissimaTrinita
197 }
198 date if date.month() == 4 && date.day() == 25 => Greeting::Liberazione,
199 date if date.month() == 5 && date.day() == 1 => Greeting::FestaDeiLavoratori,
200 date if date.month() == 6 && date.day() == 2 => Greeting::FestaDellaRepubblica,
201 #[cfg(feature = "moveable-feasts")]
202 date if date == moveable_feasts::corpus_domini_date(date.year()) => Greeting::CorpusDomini,
203 #[cfg(feature = "moveable-feasts")]
204 date if date == moveable_feasts::sacro_cuore_di_gesu_date(date.year()) => {
205 Greeting::SacroCuoreDiGesu
206 }
207 #[cfg(feature = "moveable-feasts")]
208 date if date == moveable_feasts::cuore_immacolato_di_maria_date(date.year()) => {
209 Greeting::CuoreImmacolatoDiMaria
210 }
211 date if date.month() == 3 && date.day() == 19 => Greeting::FestaDelPapa,
212 date if date.month() == 8 && date.day() == 15 => Greeting::Ferragosto,
213 date if date.month() == 10 && date.day() == 31 => Greeting::Halloween,
214 date if date.month() == 11 && date.day() == 1 => Greeting::Ognissanti,
215 date if date.month() == 11 && date.day() == 2 => Greeting::Defunti,
216 date if date.month() == 12 && date.day() == 6 => Greeting::SanNicola,
217 date if date.month() == 12 && date.day() == 7 => Greeting::SantAmbrogio,
218 date if date.month() == 12 && date.day() == 13 => Greeting::SantaLucia,
219 date if date.month() == 12 && date.day() == 8 => Greeting::ImmacolataConcenzione,
220 date if date.month() == 12 && date.day() == 24 => Greeting::VigiliaDiNatale,
221 date if date.month() == 12 && date.day() == 25 => Greeting::Natale,
222 date if date.month() == 12 && date.day() == 26 => Greeting::SantoStefano,
223 date if date.month() == 12 && date.day() == 31 => Greeting::SanSilvestro,
224 date if use_weekday => Greeting::BuonGiornoWeekday(date.weekday()),
225 _ => Greeting::BuonGiorno,
226 }
227}
228
229#[cfg(test)]
230pub fn test_log() {
231 use std::sync::Once;
232
233 use tracing_subscriber::fmt;
234
235 static INIT: Once = Once::new();
236
237 INIT.call_once(|| {
238 let subscriber = fmt::Subscriber::builder()
239 .with_max_level(tracing::Level::TRACE)
240 .with_test_writer()
241 .finish();
242
243 tracing::subscriber::set_global_default(subscriber)
244 .expect("Failed to set tracing subscriber");
245 });
246}
247
248#[cfg(test)]
249mod test {
250 use pretty_assertions::assert_eq;
251
252 use super::*;
253
254 #[test]
255 fn should_get_greeting_of_the_day_ordinary() {
256 assert_eq!(
257 greeting_of_the_day(NaiveDate::from_ymd_opt(2022, 12, 5).unwrap(), false),
258 Greeting::BuonGiorno
259 );
260 }
261
262 #[test]
263 fn should_get_greeting_of_the_day_ordinary_weekday() {
264 assert_eq!(
265 greeting_of_the_day(NaiveDate::from_ymd_opt(2022, 12, 5).unwrap(), true),
266 Greeting::BuonGiornoWeekday(chrono::Weekday::Mon)
267 );
268 }
269
270 #[test]
271 fn should_get_greeting_of_the_day_capodanno() {
272 assert_eq!(
273 greeting_of_the_day(NaiveDate::from_ymd_opt(2022, 1, 1).unwrap(), true),
274 Greeting::Capodanno
275 );
276 }
277
278 #[test]
279 fn should_get_greeting_of_the_day_epifania() {
280 assert_eq!(
281 greeting_of_the_day(NaiveDate::from_ymd_opt(2022, 1, 6).unwrap(), true),
282 Greeting::Epifania
283 );
284 }
285
286 #[test]
287 fn should_get_greeting_of_the_day_san_valentino() {
288 assert_eq!(
289 greeting_of_the_day(NaiveDate::from_ymd_opt(2022, 2, 14).unwrap(), true),
290 Greeting::SanValentino
291 );
292 }
293
294 #[test]
295 fn should_get_greeting_of_the_day_festadonne() {
296 assert_eq!(
297 greeting_of_the_day(NaiveDate::from_ymd_opt(2022, 3, 8).unwrap(), true),
298 Greeting::FestaDelleDonne
299 );
300 }
301
302 #[test]
303 #[cfg(feature = "moveable-feasts")]
304 fn should_get_greeting_of_the_day_giovedi_grasso() {
305 assert_eq!(
306 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 2, 16).unwrap(), true),
307 Greeting::GiovediGrasso
308 );
309 }
310
311 #[test]
312 #[cfg(feature = "moveable-feasts")]
313 fn should_get_greeting_of_the_day_martedi_grasso() {
314 assert_eq!(
315 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 2, 21).unwrap(), true),
316 Greeting::MartediGrasso
317 );
318 }
319
320 #[test]
321 #[cfg(feature = "moveable-feasts")]
322 fn should_get_greeting_of_the_day_ceneri() {
323 assert_eq!(
324 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 2, 22).unwrap(), true),
325 Greeting::MercolediCeneri
326 );
327 }
328
329 #[test]
330 #[cfg(feature = "moveable-feasts")]
331 fn should_get_greeting_of_the_day_palme() {
332 assert_eq!(
333 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 4, 2).unwrap(), true),
334 Greeting::DomenicaDellePalme
335 );
336 }
337
338 #[test]
339 #[cfg(feature = "moveable-feasts")]
340 fn should_get_greeting_of_the_day_pasqua() {
341 assert_eq!(
342 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 4, 9).unwrap(), true),
343 Greeting::Pasqua
344 );
345 }
346
347 #[test]
348 #[cfg(feature = "moveable-feasts")]
349 fn should_get_greeting_of_the_day_pasquetta() {
350 assert_eq!(
351 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 4, 10).unwrap(), true),
352 Greeting::Pasquetta
353 );
354 }
355
356 #[test]
357 #[cfg(feature = "moveable-feasts")]
358 fn should_get_greeting_of_the_day_ascensione() {
359 assert_eq!(
360 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 5, 21).unwrap(), true),
361 Greeting::Ascensione
362 );
363 }
364
365 #[test]
366 #[cfg(feature = "moveable-feasts")]
367 fn should_get_greeting_festa_della_mamma() {
368 assert_eq!(
369 greeting_of_the_day(NaiveDate::from_ymd_opt(2025, 5, 11).unwrap(), true),
370 Greeting::FestaDellaMamma
371 );
372 }
373
374 #[test]
375 #[cfg(feature = "moveable-feasts")]
376 fn should_get_greeting_of_the_day_trinita() {
377 assert_eq!(
378 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 6, 4).unwrap(), true),
379 Greeting::SantissimaTrinita
380 );
381 }
382
383 #[test]
384 #[cfg(feature = "moveable-feasts")]
385 fn should_get_greeting_of_the_day_corpus_domini() {
386 assert_eq!(
387 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 6, 11).unwrap(), true),
388 Greeting::CorpusDomini
389 );
390 }
391
392 #[test]
393 #[cfg(feature = "moveable-feasts")]
394 fn should_get_greeting_of_the_day_sacro_cuore() {
395 assert_eq!(
396 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 6, 16).unwrap(), true),
397 Greeting::SacroCuoreDiGesu
398 );
399 }
400
401 #[test]
402 #[cfg(feature = "moveable-feasts")]
403 fn should_get_greeting_of_the_day_cuore_immacolato() {
404 assert_eq!(
405 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 6, 17).unwrap(), true),
406 Greeting::CuoreImmacolatoDiMaria
407 );
408 }
409
410 #[test]
411 fn should_get_greeting_of_the_day_repubblica() {
412 assert_eq!(
413 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 6, 2).unwrap(), true),
414 Greeting::FestaDellaRepubblica
415 );
416 }
417
418 #[test]
419 fn should_get_greeting_of_the_day_25_aprile() {
420 assert_eq!(
421 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 4, 25).unwrap(), true),
422 Greeting::Liberazione
423 );
424 }
425
426 #[test]
427 fn should_get_greeting_of_the_day_1_maggio() {
428 assert_eq!(
429 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 5, 1).unwrap(), true),
430 Greeting::FestaDeiLavoratori
431 );
432 }
433
434 #[test]
435 fn should_get_greeting_of_the_day_ferragosto() {
436 assert_eq!(
437 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 8, 15).unwrap(), true),
438 Greeting::Ferragosto
439 );
440 }
441
442 #[test]
443 fn should_get_greeting_of_the_day_halloween() {
444 assert_eq!(
445 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 10, 31).unwrap(), true),
446 Greeting::Halloween
447 );
448 }
449
450 #[test]
451 fn should_get_greeting_of_the_day_ognissanti() {
452 assert_eq!(
453 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 11, 1).unwrap(), true),
454 Greeting::Ognissanti
455 );
456 }
457
458 #[test]
459 fn should_get_greeting_of_the_day_defunti() {
460 assert_eq!(
461 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 11, 2).unwrap(), true),
462 Greeting::Defunti
463 );
464 }
465
466 #[test]
467 fn should_get_greeting_of_the_day_sannicola() {
468 assert_eq!(
469 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 6).unwrap(), true),
470 Greeting::SanNicola
471 );
472 }
473
474 #[test]
475 fn should_get_greeting_of_the_day_santambrogio() {
476 assert_eq!(
477 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 7).unwrap(), true),
478 Greeting::SantAmbrogio
479 );
480 }
481
482 #[test]
483 fn should_get_greeting_of_the_day_immacolata() {
484 assert_eq!(
485 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 8).unwrap(), true),
486 Greeting::ImmacolataConcenzione
487 );
488 }
489
490 #[test]
491 fn should_get_greeting_of_the_day_santa_lucia() {
492 assert_eq!(
493 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 13).unwrap(), true),
494 Greeting::SantaLucia
495 );
496 }
497
498 #[test]
499 fn should_get_greeting_of_the_day_silvestro() {
500 assert_eq!(
501 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 31).unwrap(), true),
502 Greeting::SanSilvestro
503 );
504 }
505
506 #[test]
507 fn should_get_greeting_of_the_day_vigilia() {
508 assert_eq!(
509 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 24).unwrap(), true),
510 Greeting::VigiliaDiNatale
511 );
512 }
513
514 #[test]
515 fn should_get_greeting_of_the_day_natale() {
516 assert_eq!(
517 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 25).unwrap(), true),
518 Greeting::Natale
519 );
520 }
521
522 #[test]
523 fn should_get_greeting_of_the_day_santostefano() {
524 assert_eq!(
525 greeting_of_the_day(NaiveDate::from_ymd_opt(2023, 12, 26).unwrap(), true),
526 Greeting::SantoStefano
527 );
528 }
529}