ta import-tdb2

This commit is contained in:
Dustin J. Mitchell
2022-01-08 22:11:27 +00:00
parent 162a9eae95
commit 69d052603d
8 changed files with 200 additions and 4 deletions

View File

@@ -0,0 +1 @@
[description:"&open;TEST&close; foo" entry:"1554074416" modified:"1554074416" priority:"M" status:"completed" uuid:"4578fb67-359b-4483-afe4-fef15925ccd6"]

View File

@@ -4,10 +4,13 @@ use serde::{self, Deserialize, Deserializer};
use serde_json::Value;
use std::collections::HashMap;
use taskchampion::{Replica, Uuid};
use termcolor::WriteColor;
use termcolor::{Color, ColorSpec, WriteColor};
pub(crate) fn execute<W: WriteColor>(w: &mut W, replica: &mut Replica) -> Result<(), crate::Error> {
w.set_color(ColorSpec::new().set_bold(true))?;
writeln!(w, "Importing tasks from stdin.")?;
w.reset()?;
let mut tasks: Vec<HashMap<String, Value>> =
serde_json::from_reader(std::io::stdin()).map_err(|_| anyhow!("Invalid JSON"))?;
@@ -15,7 +18,10 @@ pub(crate) fn execute<W: WriteColor>(w: &mut W, replica: &mut Replica) -> Result
import_task(w, replica, task_json)?;
}
w.set_color(ColorSpec::new().set_bold(true))?;
writeln!(w, "{} tasks imported.", tasks.len())?;
w.reset()?;
Ok(())
}
@@ -130,10 +136,12 @@ fn import_task<W: WriteColor>(
}
}
w.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
write!(w, "{}", uuid)?;
w.reset()?;
writeln!(
w,
"{} {}",
uuid,
" {}",
description.unwrap_or_else(|| "(no description)".into())
)?;

View File

@@ -0,0 +1,142 @@
use crate::tdb2;
use anyhow::anyhow;
use std::fs;
use std::path::PathBuf;
use taskchampion::{Replica, Uuid};
use termcolor::{Color, ColorSpec, WriteColor};
pub(crate) fn execute<W: WriteColor>(
w: &mut W,
replica: &mut Replica,
path: &str,
) -> Result<(), crate::Error> {
let path: PathBuf = path.into();
let mut count = 0;
for file in &["pending.data", "completed.data"] {
let file = path.join(file);
w.set_color(ColorSpec::new().set_bold(true))?;
writeln!(w, "Importing tasks from {:?}.", file)?;
w.reset()?;
let data = fs::read_to_string(file)?;
let content =
tdb2::File::from_str(&data).map_err(|_| anyhow!("Could not parse TDB2 file format"))?;
count += content.lines.len();
for line in content.lines {
import_task(w, replica, line)?;
}
}
w.set_color(ColorSpec::new().set_bold(true))?;
writeln!(w, "{} tasks imported.", count)?;
w.reset()?;
Ok(())
}
fn import_task<W: WriteColor>(
w: &mut W,
replica: &mut Replica,
mut line: tdb2::Line,
) -> anyhow::Result<()> {
let mut uuid = None;
for attr in line.attrs.iter() {
if &attr.name == "uuid" {
uuid = Some(Uuid::parse_str(&attr.value)?);
break;
}
}
let uuid = uuid.ok_or_else(|| anyhow!("task has no uuid"))?;
replica.create_task(uuid)?;
let mut description = None;
for attr in line.attrs.drain(..) {
// oddly, TaskWarrior represents [ and ] with their HTML entity equivalents
let value = attr.value.replace("&open;", "[").replace("&close;", "]");
match attr.name.as_ref() {
// `uuid` was already handled
"uuid" => {}
// everything else is inserted directly
_ => {
if attr.name == "description" {
// keep a copy of the description for console output
description = Some(value.clone());
}
replica.update_task(uuid, attr.name, Some(value))?;
}
}
}
w.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
write!(w, "{}", uuid)?;
w.reset()?;
writeln!(
w,
" {}",
description.unwrap_or_else(|| "(no description)".into())
)?;
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use crate::invocation::test::*;
use chrono::{TimeZone, Utc};
use pretty_assertions::assert_eq;
use std::convert::TryInto;
use taskchampion::{Priority, Status};
use tempfile::TempDir;
#[test]
fn test_import() -> anyhow::Result<()> {
let mut w = test_writer();
let mut replica = test_replica();
let tmp_dir = TempDir::new()?;
fs::write(
tmp_dir.path().join("pending.data"),
include_bytes!("pending.data"),
)?;
fs::write(
tmp_dir.path().join("completed.data"),
include_bytes!("completed.data"),
)?;
execute(&mut w, &mut replica, tmp_dir.path().to_str().unwrap())?;
let task = replica
.get_task(Uuid::parse_str("f19086c2-1f8d-4a6c-9b8d-f94901fb8e62").unwrap())
.unwrap()
.unwrap();
assert_eq!(task.get_description(), "snake 🐍");
assert_eq!(task.get_status(), Status::Pending);
assert_eq!(task.get_priority(), Priority::M);
assert_eq!(task.get_wait(), None);
assert_eq!(
task.get_modified(),
Some(Utc.ymd(2022, 1, 8).and_hms(19, 33, 5))
);
assert!(task.has_tag(&"reptile".try_into().unwrap()));
assert!(!task.has_tag(&"COMPLETED".try_into().unwrap()));
let task = replica
.get_task(Uuid::parse_str("4578fb67-359b-4483-afe4-fef15925ccd6").unwrap())
.unwrap()
.unwrap();
assert_eq!(task.get_description(), "[TEST] foo");
assert_eq!(task.get_status(), Status::Completed);
assert_eq!(task.get_priority(), Priority::M);
assert_eq!(task.get_wait(), None);
assert_eq!(
task.get_modified(),
Some(Utc.ymd(2019, 3, 31).and_hms(23, 20, 16))
);
assert!(!task.has_tag(&"reptile".try_into().unwrap()));
assert!(task.has_tag(&"COMPLETED".try_into().unwrap()));
Ok(())
}
}

View File

@@ -5,6 +5,7 @@ pub(crate) mod config;
pub(crate) mod gc;
pub(crate) mod help;
pub(crate) mod import;
pub(crate) mod import_tdb2;
pub(crate) mod info;
pub(crate) mod modify;
pub(crate) mod report;

View File

@@ -0,0 +1 @@
[description:"snake 🐍" entry:"1641670385" modified:"1641670385" priority:"M" status:"pending" tag_reptile:"" uuid:"f19086c2-1f8d-4a6c-9b8d-f94901fb8e62"]

View File

@@ -97,6 +97,13 @@ pub(crate) fn invoke(command: Command, settings: Settings) -> Result<(), crate::
return cmd::import::execute(&mut w, &mut replica);
}
Command {
subcommand: Subcommand::ImportTDB2 { path },
..
} => {
return cmd::import_tdb2::execute(&mut w, &mut replica, path.as_ref());
}
Command {
subcommand: Subcommand::Undo,
..