use chrono::{DateTime, FixedOffset};
use std::env;
use std::fs;
use std::io::{Error, ErrorKind, Write};
use std::path::PathBuf;
use crate::entry;
pub fn get_tsv_path(tsv: &Option<std::path::PathBuf>) -> Result<PathBuf, Error> {
match tsv {
Some(path) => is_existing_file(path, "--tsv "),
_ => {
let idid_tsv = "ididTSV";
if let Ok(value) = env::var(idid_tsv) {
let prefix = format!("${} ", idid_tsv);
return is_existing_file(&PathBuf::from(value), &prefix);
}
let env_xdg = "XDG_DATA_HOME";
match env::var(env_xdg) {
Err(e) => Err(Error::new(
ErrorKind::NotFound,
format!("${} does not exist: {}", env_xdg, e),
)),
Ok(value) => {
let prefix = format!("${} ", env_xdg);
let mut path = PathBuf::from(value);
path.push("idid");
path.push("idid.tsv");
match is_existing_file(&path, &prefix) {
Ok(path) => Ok(path),
Err(_e) => {
match fs::File::create(&path) {
Ok(_fc) => Ok(path),
Err(e) => Err(e),
}
}
}
}
}
}
}
}
fn is_existing_file(path: &std::path::Path, prefix: &str) -> Result<PathBuf, Error> {
let file_path = path.to_string_lossy().clone();
if path.exists() {
if path.is_file() {
Ok(path.to_path_buf())
} else {
Err(Error::new(
ErrorKind::InvalidInput,
format!("{}not a file: {}", prefix, file_path),
))
}
} else {
Err(Error::new(
ErrorKind::NotFound,
format!("{}does not exist: {}", prefix, file_path),
))
}
}
pub fn write_to_tsv(path: &str, timestamp: &DateTime<FixedOffset>, user_text: Option<&str>) {
let mut file = fs::OpenOptions::new()
.create(true)
.append(true)
.open(path)
.expect("Failed to open file");
file.write_all(
timestamp
.to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
.as_bytes(),
)
.expect("Failed to write timestamp to file");
file.write_all(b"\t").expect("Failed to write tab");
let text = match user_text {
Some(text) => text,
None => entry::START_RECORDING,
};
file.write_all(text.as_bytes())
.expect("Failed to write to file");
file.write_all(b"\n").expect("Failed to write line-feed");
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use std::fs;
use std::io::Error;
use std::path::PathBuf;
use tempfile::Builder;
#[test]
#[ignore]
fn test_get_tsv_path_ididtsv() -> Result<(), Error> {
let env_vars = ["ididTSV", "XDG_DATA_HOME"];
let saved_values: Vec<Option<String>> = env_vars
.iter()
.map(|&var| save_environment_variable(var))
.collect();
let temp_file = Builder::new().suffix(".txt").tempfile()?;
let file_path = temp_file.path().to_owned();
env::set_var(env_vars[0], &file_path);
env::remove_var(env_vars[1]);
let result = get_tsv_path(&None::<PathBuf>);
for (var, saved_value) in env_vars.iter().zip(saved_values) {
restore_environment_variable(var, saved_value);
}
drop(temp_file);
let _ = std::fs::remove_file(&file_path);
assert!(result.is_ok());
Ok(())
}
#[test]
#[ignore]
fn test_get_tsv_path_xdg() -> Result<(), Error> {
let env_vars = ["ididTSV", "XDG_DATA_HOME"];
let saved_values: Vec<Option<String>> = env_vars
.iter()
.map(|&var| save_environment_variable(var))
.collect();
let temp_dir = env::temp_dir();
let idid_dir = temp_dir.join("idid");
fs::create_dir_all(&idid_dir)?;
let file_path = idid_dir.join("idid.tsv");
let temp_file = fs::File::create(&file_path)?;
drop(temp_file);
env::set_var(env_vars[1], &temp_dir);
env::remove_var(env_vars[0]);
let result = get_tsv_path(&None::<PathBuf>);
for (var, saved_value) in env_vars.iter().zip(saved_values) {
restore_environment_variable(var, saved_value);
}
std::fs::remove_file(&file_path)?;
assert!(result.is_ok());
Ok(())
}
fn save_environment_variable(name: &str) -> Option<String> {
match env::var(name) {
Ok(value) => Some(value),
Err(_) => None,
}
}
fn restore_environment_variable(name: &str, saved_value: Option<String>) {
match saved_value {
Some(value) => env::set_var(name, &value),
None => env::remove_var(name),
}
}
}