/ src / types.rs
types.rs
  1  use chrono::{DateTime, Utc};
  2  use serde::{Deserialize, Serialize};
  3  
  4  #[derive(Copy, Clone, PartialEq)]
  5  pub enum StorySorting {
  6      Top,
  7      New,
  8      Best,
  9      Show,
 10      Ask,
 11      Job,
 12  }
 13  
 14  const TOP: &'static str = "top";
 15  const BEST: &'static str = "best";
 16  const NEW: &'static str = "new";
 17  const SHOW: &'static str = "show";
 18  const ASK: &'static str = "ask";
 19  const JOB: &'static str = "job";
 20  
 21  impl StorySorting {
 22      /// return all of the story sorting possible
 23      pub fn all() -> Vec<Self> {
 24          vec![
 25              StorySorting::Top,
 26              StorySorting::Best,
 27              StorySorting::New,
 28              StorySorting::Show,
 29              StorySorting::Ask,
 30              StorySorting::Job,
 31          ]
 32      }
 33      /// match url to StorySorting (supports both hash and path routing)
 34      pub fn from_url(url: &str) -> Option<Self> {
 35          let hash_url = url.strip_prefix("#").unwrap_or(url);
 36          Self::all().into_iter().find(|s| hash_url == s.to_str() || url == s.to_url())
 37      }
 38      /// return the str for assembling paths in warp
 39      pub fn to_str(&self) -> &str {
 40          match self {
 41              StorySorting::Top => TOP,
 42              StorySorting::Best => BEST,
 43              StorySorting::New => NEW,
 44              StorySorting::Show => SHOW,
 45              StorySorting::Ask => ASK,
 46              StorySorting::Job => JOB,
 47          }
 48      }
 49  
 50      pub fn to_url(&self) -> String {
 51          format!("#{}", self.to_str())
 52      }
 53  }
 54  
 55  impl Default for StorySorting {
 56      fn default() -> Self {
 57          StorySorting::Top
 58      }
 59  }
 60  
 61  #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
 62  pub struct StoryPageData {
 63      pub id: i64,
 64      pub title: String,
 65      pub url: Option<String>,
 66      pub text: Option<String>,
 67      #[serde(default)]
 68      pub by: String,
 69      #[serde(default)]
 70      pub score: i64,
 71      #[serde(default)]
 72      pub descendants: i64,
 73      #[serde(with = "chrono::serde::ts_seconds")]
 74      pub time: DateTime<Utc>,
 75      #[serde(default)]
 76      pub kids: Vec<i64>,
 77      pub r#type: String,
 78      #[serde(default)]
 79      pub comments: Vec<Comment>,
 80  }
 81  
 82  impl StoryPageData {
 83      /// derive a preview of this StoragePageData
 84      pub fn preview(&self) -> StoryItem {
 85          StoryItem {
 86              id: self.id,
 87              title: self.title.to_owned(),
 88              url: self.url.to_owned(),
 89              text: self.text.to_owned(),
 90              by: self.by.to_owned(),
 91              score: self.score,
 92              descendants: self.descendants,
 93              time: self.time.to_owned(),
 94              kids: self.kids.to_owned(),
 95              r#type: self.r#type.to_owned(),
 96          }
 97      }
 98  }
 99  
100  #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
101  pub struct Comment {
102      pub id: i64,
103      /// there will be no by field if the comment was deleted
104      #[serde(default)]
105      pub by: String,
106      #[serde(default)]
107      pub text: String,
108      #[serde(with = "chrono::serde::ts_seconds")]
109      pub time: DateTime<Utc>,
110      #[serde(default)]
111      pub kids: Vec<i64>,
112      #[serde(default)]
113      pub sub_comments: Vec<Comment>,
114      pub r#type: String,
115  }
116  
117  impl Comment {
118      /// attempt to extract comment id from url (supports both hash and path routing)
119      pub fn id_from_url(url: &str) -> Option<i64> {
120          let target_url = url.strip_prefix("#").unwrap_or(url);
121          if target_url.starts_with("comment/") || url.starts_with("/comment") {
122              let splinters = if target_url.starts_with("comment/") {
123                  target_url.split("/").collect::<Vec<_>>()
124              } else {
125                  url.split("/").collect::<Vec<_>>()
126              };
127              if splinters.len() >= 2 {
128                  if target_url.starts_with("comment/") {
129                      splinters[1].parse::<i64>().ok()
130                  } else {
131                      splinters[2].parse::<i64>().ok()
132                  }
133              } else {
134                  None
135              }
136          } else {
137              None
138          }
139      }
140  
141      pub fn to_url(comment_id: i64) -> String {
142          format!("#comment/{}", comment_id)
143      }
144  }
145  
146  #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
147  pub struct StoryItem {
148      pub id: i64,
149      pub title: String,
150      pub url: Option<String>,
151      pub text: Option<String>,
152      #[serde(default)]
153      pub by: String,
154      #[serde(default)]
155      pub score: i64,
156      #[serde(default)]
157      pub descendants: i64,
158      #[serde(with = "chrono::serde::ts_seconds")]
159      pub time: DateTime<Utc>,
160      #[serde(default)]
161      pub kids: Vec<i64>,
162      pub r#type: String,
163  }
164  
165  impl StoryItem {
166      /// attempt to extract story id from url (supports both hash and path routing)
167      pub fn id_from_url(url: &str) -> Option<i64> {
168          let target_url = url.strip_prefix("#").unwrap_or(url);
169          if target_url.starts_with("item/") || url.starts_with("/item") {
170              let splinters = if target_url.starts_with("item/") {
171                  target_url.split("/").collect::<Vec<_>>()
172              } else {
173                  url.split("/").collect::<Vec<_>>()
174              };
175              if splinters.len() >= 2 {
176                  if target_url.starts_with("item/") {
177                      splinters[1].parse::<i64>().ok()
178                  } else {
179                      splinters[2].parse::<i64>().ok()
180                  }
181              } else {
182                  None
183              }
184          } else {
185              None
186          }
187      }
188  
189      pub fn to_url(story_id: i64) -> String {
190          format!("#item/{}", story_id)
191      }
192  }
193  
194  #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
195  pub struct UserData {
196      pub id: String,
197      pub karma: i64,
198      #[serde(default)]
199      pub about: String,
200      #[serde(default)]
201      pub submitted: Vec<i64>,
202      #[serde(default)]
203      pub stories: Vec<StoryItem>,
204  }
205  
206  impl UserData {
207      /// attempt to extract user id from url (supports both hash and path routing)
208      pub fn id_from_url(url: &str) -> Option<String> {
209          let target_url = url.strip_prefix("#").unwrap_or(url);
210          if target_url.starts_with("user/") || url.starts_with("/user") {
211              let splinters = if target_url.starts_with("user/") {
212                  target_url.split("/").collect::<Vec<_>>()
213              } else {
214                  url.split("/").collect::<Vec<_>>()
215              };
216              if splinters.len() >= 2 {
217                  if target_url.starts_with("user/") {
218                      Some(splinters[1].to_string())
219                  } else {
220                      Some(splinters[2].to_string())
221                  }
222              } else {
223                  None
224              }
225          } else {
226              None
227          }
228      }
229  
230      pub fn to_url(username: &str) -> String {
231          format!("#user/{}", username)
232      }
233  }