Web-based Calendar Aggregator https://ticker.c3d2.de/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mod.rs 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. use std::collections::{HashMap, HashSet};
  2. use std::fmt::Write;
  3. use std::str::FromStr;
  4. use chrono::{NaiveDate, NaiveDateTime};
  5. mod tokenizer;
  6. mod parser;
  7. pub use parser::Parser;
  8. pub trait GetValue<'a, R: 'a> {
  9. fn get(&'a self, key: &'_ str) -> Option<R>;
  10. }
  11. pub type Props = Vec<(String, String)>;
  12. #[derive(Debug, PartialEq)]
  13. pub struct Object {
  14. pub name: String,
  15. pub content: HashMap<String, Vec<(Props, String)>>,
  16. pub children: Vec<Box<Object>>,
  17. }
  18. impl<'a> GetValue<'a, &'a str> for Object {
  19. fn get(&'a self, key: &'_ str) -> Option<&'a str> {
  20. self.content.get(key)
  21. .and_then(|pairs| pairs.first().map(|(_props, value)| value.as_str()))
  22. }
  23. }
  24. impl<'a> GetValue<'a, &'a (Props, String)> for Object {
  25. fn get(&'a self, key: &'_ str) -> Option<&'a (Props, String)> {
  26. self.content.get(key)
  27. .and_then(|pairs| pairs.first())
  28. }
  29. }
  30. impl<'a> GetValue<'a, &'a [(Props, String)]> for Object {
  31. fn get(&'a self, key: &'_ str) -> Option<&'a [(Props, String)]> {
  32. self.content.get(key)
  33. .map(|data| data.as_slice())
  34. }
  35. }
  36. impl<'a> GetValue<'a, String> for Object {
  37. fn get(&self, key: &'_ str) -> Option<String> {
  38. self.content.get(key)
  39. .and_then(|pairs| pairs.first().map(|(_props, value)| value.clone()))
  40. }
  41. }
  42. // TODO: TZID, offset
  43. impl<'a> GetValue<'a, Timestamp> for Object {
  44. fn get(&self, key: &'_ str) -> Option<Timestamp> {
  45. let s = self.get(key)?;
  46. Timestamp::from_str(s)
  47. .map_err(|e| println!("Cannot parse date: {:?}", e))
  48. .ok()
  49. }
  50. }
  51. impl Object {
  52. pub fn fmt_rrule(&self) -> Result<String, std::fmt::Error> {
  53. let rrule_keys = [
  54. "DTSTART",
  55. "RRULE", "RDATE",
  56. "EXRULE", "EXDATE",
  57. ].iter().cloned().collect::<HashSet<_>>();
  58. let mut s = String::new();
  59. for (name, values) in &self.content {
  60. if ! rrule_keys.contains(name.as_str()) {
  61. continue;
  62. }
  63. for (props, value) in values {
  64. write!(s, "{}", name)?;
  65. for (key, prop) in props {
  66. write!(s, ";{}={}", key, prop)?;
  67. }
  68. writeln!(s, ":{}", value)?;
  69. }
  70. }
  71. Ok(s)
  72. }
  73. }
  74. #[derive(Debug, Clone)]
  75. pub enum Timestamp {
  76. Date(NaiveDate),
  77. DateTime(NaiveDateTime),
  78. }
  79. impl FromStr for Timestamp {
  80. type Err = String;
  81. fn from_str(s: &'_ str) -> Result<Self, Self::Err> {
  82. NaiveDateTime::parse_from_str(s, "%Y%m%dT%H%M%SZ")
  83. .or_else(|_| NaiveDateTime::parse_from_str(s, "%Y%m%dT%H%M%S"))
  84. .map(Timestamp::DateTime)
  85. .or_else(|_|
  86. NaiveDate::parse_from_str(s, "%Y%m%d")
  87. .map(Timestamp::Date)
  88. )
  89. .map_err(|e| format!("Cannot parse date: {:?}", e))
  90. }
  91. }
  92. impl Timestamp {
  93. pub fn or_hms(self, hour: u32, min: u32, sec: u32) -> NaiveDateTime {
  94. match self {
  95. Timestamp::Date(date) => date.and_hms(hour, min, sec),
  96. Timestamp::DateTime(date_time) => date_time,
  97. }
  98. }
  99. }