use std::{fs::File, io::Write};
use chrono::prelude::*;
use crate::{errors::DiaryError, utils::editing, Diary, EntryContent};
pub struct AddOptions {
pub tag: Option<String>,
pub content: Option<String>,
}
fn add_content(mut file: File, content: String, tag: Option<String>) -> Result<(), DiaryError> {
if content.is_empty() {
return Err(DiaryError::NoContent);
}
if let Some(tag) = tag {
file.write_all(tag.as_bytes())?;
}
editing::add_user_content_to_file(&mut file, content)?;
Ok(())
}
pub fn add(
opts: &AddOptions,
diary: &Diary,
date: &DateTime<Local>,
string_getter: editing::StringGetter,
) -> Result<(), DiaryError> {
let file = match diary.get_entry_file(date) {
Ok(file) => file,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Err(DiaryError::NoEntry { source: Some(e) })
}
Err(e) => return Err(e.into()), };
let content = match &opts.content {
Some(content) => content.to_owned() + "\n",
None => string_getter("".to_owned())?,
};
let tag_result = opts
.tag
.as_ref()
.map(|tag| diary.file_type().tag(tag.to_string()));
add_content(file, content, tag_result)
}
#[cfg(test)]
mod test {
use std::fs;
use chrono::{Local, TimeZone};
use crate::{
ops::{
add::{add, AddOptions},
testing,
},
utils::editing::test::{test_empty_string_getter, test_string_getter},
Diary,
};
#[test]
fn quick_add() {
let config = testing::temp_config();
testing::default_init(config.diary_path());
let entry_date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
testing::new_entry(&config, &entry_date);
let diary = Diary::from_config(&config).unwrap();
let opts = AddOptions {
tag: None,
content: Some("testing quick add".to_owned()),
};
add(&opts, &diary, &entry_date, test_string_getter).unwrap();
let diary_file = Diary::from_config(&config).unwrap();
let entry_path = diary_file.get_entry_path(&entry_date);
let content = fs::read_to_string(entry_path).unwrap();
assert!(content.contains("testing quick add"));
}
#[test]
fn add_no_tag() {
let config = testing::temp_config();
testing::default_init(config.diary_path());
let entry_date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
testing::new_entry(&config, &entry_date);
let diary = Diary::from_config(&config).unwrap();
let opts = AddOptions {
tag: None,
content: None,
};
add(&opts, &diary, &entry_date, test_string_getter).unwrap();
let diary_file = Diary::from_config(&config).unwrap();
let entry_path = diary_file.get_entry_path(&entry_date);
let content = fs::read_to_string(entry_path).unwrap();
assert!(content.contains("Test content"));
assert!(!content.contains("\n\n\n"))
}
#[test]
fn add_with_tag() {
let config = testing::temp_config();
testing::default_init(config.diary_path());
let entry_date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
testing::new_entry(&config, &entry_date);
let diary = Diary::from_config(&config).unwrap();
let opts = AddOptions {
tag: Some("Tag".to_owned()),
content: None,
};
add(&opts, &diary, &entry_date, test_string_getter).unwrap();
let entry_path = diary.get_entry_path(&entry_date);
let content = fs::read_to_string(entry_path).unwrap();
assert!(content.contains("Test content"));
assert!(content.contains("Tag"));
}
#[test]
#[should_panic(expected = "value: NoContent")]
fn add_empty_string() {
let config = testing::temp_config();
testing::default_init(config.diary_path());
let entry_date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
testing::new_entry(&config, &entry_date);
let diary = Diary::from_config(&config).unwrap();
let opts = AddOptions {
tag: Some("Tag".to_owned()),
content: None,
};
add(&opts, &diary, &entry_date, test_empty_string_getter).unwrap();
}
#[test]
#[should_panic(expected = "value: NoEntry")]
fn add_to_nonexistent_file() {
let config = testing::temp_config();
testing::default_init(config.diary_path());
let diary = Diary::from_config(&config).unwrap();
let entry_date = Local.with_ymd_and_hms(2021, 11, 6, 0, 0, 0).unwrap();
let opts = AddOptions {
tag: Some("Tag".to_owned()),
content: None,
};
add(&opts, &diary, &entry_date, test_string_getter).unwrap();
}
}