rocket-pinboard/src/models/list.rs

138 lines
4.2 KiB
Rust

use comrak;
use glob::glob;
use inflector::Inflector;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::time::SystemTime;
#[derive(Clone, Debug, Serialize, Deserialize)]
/// Structure for representing a wish list
pub struct List {
/// The ID of the list (unique string)
pub id: String,
/// The index of the list (unique number)
pub index: i8,
/// The raw list data
pub data: String,
/// The time the list was last modified
pub mtime: SystemTime,
/// The name of the list, i.e. the person it is for
pub name: String,
/// The path to the list file
path: PathBuf
}
impl List {
pub fn to_html(&self) -> String {
let mut options = comrak::ComrakOptions::default();
options.ext_strikethrough = true;
options.ext_tagfilter = true;
options.ext_autolink = true;
options.ext_tasklist = true;
comrak::markdown_to_html(&self.data, &options)
}
pub fn update_data(&mut self, data : &String) {
let mut file = File::create(&self.path)
.expect(&format!("Cannot open list file {}",
self.path.to_str().unwrap()));
file.write_all(data.as_bytes())
.expect(&format!("Cannot write list file {}",
self.path.to_str().unwrap()));
self.data = data.clone();
let metadata = file.metadata().unwrap();
self.mtime = metadata.modified().unwrap();
}
pub fn load_all(list_path: Option<&str>) -> Vec<Self> {
let mut lists : Vec<List> = vec![];
let mut index = 0;
let path_glob = match list_path {
Some(dir) => format!("{}/lists/*.list", dir),
None => format!("lists/*.list")
};
for entry in glob(path_glob.as_str()).unwrap().filter_map(Result::ok) {
let file_name = entry.file_name().unwrap().to_str().unwrap();
let name = match file_name.find('.') {
Some(index) => &file_name[0..index],
None => "unknown"
};
let mut data = String::new();
let mut file = File::open(&entry)
.expect(&format!("Cannot open list file {}", file_name));
file.read_to_string(&mut data)
.expect(&format!("Cannot read list file {}", file_name));
let metadata = file.metadata()
.expect(&format!("Cannot get metadata of list file {}", file_name));
let mut list = List {
id: String::from(name),
index : index,
data: data,
mtime: metadata.modified().unwrap(),
name: String::from(name).to_title_case(),
path: entry.clone()
};
lists.push(list);
index += 1;
}
lists
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn loads_all_lists() {
let lists = List::load_all(Some("test"));
let list_ids: Vec<&str> = lists.iter()
.map(|list| list.id.as_ref()).collect();
assert_eq!(list_ids, vec!["test", "updatable"]);
}
#[test]
fn converts_to_html() {
let lists = List::load_all(Some("test"));
let list = lists.iter()
.find(|list| list.id == "test")
.unwrap();
assert_eq!(list.to_html(), r#"<p>This is a test list</p>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
"#);
}
#[test]
fn updates_data() {
let mut lists = List::load_all(Some("test"));
let list = lists.iter_mut()
.find(|list| list.id == "updatable")
.unwrap();
assert_eq!(list.data, "Some content");
// Update the data van verify it has changed
let new_data = "New content";
list.update_data(&String::from(new_data));
assert_eq!(list.data, new_data);
// Verify that the data is written to the file of the list by
// loading them again
let mut lists = List::load_all(Some("test"));
let list = lists.iter_mut()
.find(|list| list.id == "updatable")
.unwrap();
assert_eq!(list.data, new_data);
// ... and change it back again
list.update_data(&String::from("Some content"));
}
}