mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
taud: Finish new RPC port.
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -5952,10 +5952,11 @@ dependencies = [
|
||||
"log",
|
||||
"prettytable-rs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simplelog",
|
||||
"taud",
|
||||
"term_grid",
|
||||
"textwrap 0.16.0",
|
||||
"tinyjson",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ repository = "https://github.com/darkrenaissance/darkfi"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.147"
|
||||
darkfi = { path = "../../../", features = ["rpc"]}
|
||||
darkfi = {path = "../../../", features = ["rpc"]}
|
||||
taud = {path = "../taud"}
|
||||
|
||||
# Async
|
||||
async-std = {version = "1.12.0", features = ["attributes"]}
|
||||
@@ -29,4 +30,4 @@ url = "2.4.0"
|
||||
# Encoding and parsing
|
||||
clap = {version = "4.3.22", features = ["derive"]}
|
||||
serde = {version = "1.0.183", features = ["derive"]}
|
||||
serde_json = "1.0.105"
|
||||
tinyjson = "2.5.1"
|
||||
|
||||
@@ -23,7 +23,6 @@ use std::{
|
||||
|
||||
use chrono::{Datelike, Local, TimeZone, Utc};
|
||||
use log::error;
|
||||
use serde_json::Value;
|
||||
|
||||
use darkfi::Result;
|
||||
|
||||
@@ -38,7 +37,8 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
|
||||
// Filter by state.
|
||||
_ if filter.contains("state:") => {
|
||||
let kv: Vec<&str> = filter.split(':').collect();
|
||||
if let Some(state) = Value::from(kv[1]).as_str() {
|
||||
//if let Some(state) = Value::from(kv[1]).as_str() {
|
||||
if let Some(state) = Some(kv[1]) {
|
||||
match state {
|
||||
"open" => tasks.retain(|task| task.state == State::Open.to_string()),
|
||||
"start" => tasks.retain(|task| task.state == State::Start.to_string()),
|
||||
@@ -59,7 +59,8 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
|
||||
_ if filter.contains("month:") => {
|
||||
let kv: Vec<&str> = filter.split(':').collect();
|
||||
if kv.len() == 2 {
|
||||
if let Some(value) = Value::from(kv[1]).as_str() {
|
||||
//if let Some(value) = Value::from(kv[1]).as_str() {
|
||||
if let Some(value) = Some(kv[1]) {
|
||||
if value.len() != 4 || value.parse::<u32>().is_err() {
|
||||
error!(
|
||||
"Please provide month date as \"MMYY\" (e.g. 0922 for September 2022)"
|
||||
@@ -71,7 +72,7 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
|
||||
|
||||
let year = year + (Utc::now().year() / 100) * 100;
|
||||
tasks.retain(|task| {
|
||||
let date = task.created_at;
|
||||
let date = task.created_at.0;
|
||||
let task_date = Utc.timestamp_nanos(date.try_into().unwrap()).date_naive();
|
||||
task_date.month() == month && task_date.year() == year
|
||||
})
|
||||
@@ -89,7 +90,8 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
|
||||
_ if filter.contains("project:") => {
|
||||
let kv: Vec<&str> = filter.split(':').collect();
|
||||
if kv.len() == 2 {
|
||||
if let Some(value) = Value::from(kv[1]).as_str() {
|
||||
// OLD: if let Some(value) = Value::from(kv[1]).as_str() {
|
||||
if let Some(value) = Some(kv[1]) {
|
||||
if value.is_empty() {
|
||||
tasks.retain(|task| task.project.is_empty())
|
||||
} else {
|
||||
@@ -128,7 +130,9 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
|
||||
};
|
||||
|
||||
if kv.len() == 2 {
|
||||
if let Some(value) = Value::from(kv[1]).as_str() {
|
||||
// OLD: let Value::from(kv[1]).as_str();
|
||||
let value = Some(kv[1]);
|
||||
if let Some(value) = value {
|
||||
if value.is_empty() {
|
||||
tasks.retain(|task| task.due.is_none())
|
||||
} else {
|
||||
@@ -140,7 +144,7 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
|
||||
};
|
||||
|
||||
tasks.retain(|task| {
|
||||
let date = task.due.unwrap_or(0);
|
||||
let date = if let Some(due) = task.due { due.0 } else { 0 };
|
||||
let task_date =
|
||||
Utc.timestamp_nanos(date.try_into().unwrap()).date_naive();
|
||||
|
||||
@@ -181,25 +185,25 @@ pub fn no_filter_warn() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ids(filters: &mut Vec<String>) -> Result<Vec<u64>> {
|
||||
pub fn get_ids(filters: &mut Vec<String>) -> Result<Vec<u32>> {
|
||||
let mut vec_ids = vec![];
|
||||
let mut matching_id = String::new();
|
||||
if let Some(index) = filters.iter().position(|t| {
|
||||
t.parse::<u64>().is_ok() || !t.contains(':') && (t.contains(',') || t.contains('-'))
|
||||
t.parse::<u32>().is_ok() || !t.contains(':') && (t.contains(',') || t.contains('-'))
|
||||
}) {
|
||||
matching_id.push_str(&filters.remove(index));
|
||||
}
|
||||
|
||||
match matching_id {
|
||||
_ if matching_id.parse::<u64>().is_ok() => {
|
||||
let id = matching_id.parse::<u64>().unwrap();
|
||||
_ if matching_id.parse::<u32>().is_ok() => {
|
||||
let id = matching_id.parse::<u32>().unwrap();
|
||||
vec_ids.push(id)
|
||||
}
|
||||
_ if !matching_id.contains(':') &&
|
||||
(matching_id.contains(',') || matching_id.contains('-')) =>
|
||||
{
|
||||
let num = matching_id.replace(&[',', '-'][..], "");
|
||||
if num.parse::<u64>().is_err() {
|
||||
if num.parse::<u32>().is_err() {
|
||||
error!("Invalid ID number");
|
||||
exit(1)
|
||||
}
|
||||
@@ -209,17 +213,17 @@ pub fn get_ids(filters: &mut Vec<String>) -> Result<Vec<u64>> {
|
||||
if id.contains('-') {
|
||||
let range: Vec<&str> = id.split('-').collect();
|
||||
let range =
|
||||
range[0].parse::<u64>().unwrap()..=range[1].parse::<u64>().unwrap();
|
||||
range[0].parse::<u32>().unwrap()..=range[1].parse::<u32>().unwrap();
|
||||
for rid in range {
|
||||
vec_ids.push(rid)
|
||||
}
|
||||
} else {
|
||||
vec_ids.push(id.parse::<u64>().unwrap())
|
||||
vec_ids.push(id.parse::<u32>().unwrap())
|
||||
}
|
||||
}
|
||||
} else if matching_id.contains('-') {
|
||||
let range: Vec<&str> = matching_id.split('-').collect();
|
||||
let range = range[0].parse::<u64>().unwrap()..=range[1].parse::<u64>().unwrap();
|
||||
let range = range[0].parse::<u32>().unwrap()..=range[1].parse::<u32>().unwrap();
|
||||
for rid in range {
|
||||
vec_ids.push(rid)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ use primitives::{task_from_cli, State, TaskEvent};
|
||||
use util::{due_as_timestamp, prompt_text};
|
||||
use view::{print_task_info, print_task_list};
|
||||
|
||||
use crate::primitives::TaskInfo;
|
||||
use taud::task_info::TaskInfo;
|
||||
|
||||
const DEFAULT_PATH: &str = "~/tau_exported_tasks";
|
||||
|
||||
@@ -375,7 +375,7 @@ async fn main() -> Result<()> {
|
||||
Some(date) => {
|
||||
let ts =
|
||||
to_naivedate(date.clone())?.and_hms_opt(12, 0, 0).unwrap().timestamp();
|
||||
let tasks = tau.get_stop_tasks(Some(ts)).await?;
|
||||
let tasks = tau.get_stop_tasks(Some(ts.try_into().unwrap())).await?;
|
||||
drawdown(date, tasks, assignee)?;
|
||||
}
|
||||
None => {
|
||||
|
||||
@@ -16,58 +16,12 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
use darkfi::{util::time::Timestamp, Error, Result};
|
||||
use darkfi::{util::time::Timestamp, Result};
|
||||
|
||||
use crate::due_as_timestamp;
|
||||
pub(crate) use taud::task_info::{State, TaskEvent, TaskInfo};
|
||||
|
||||
pub enum State {
|
||||
Open,
|
||||
Start,
|
||||
Pause,
|
||||
Stop,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn is_start(&self) -> bool {
|
||||
matches!(*self, Self::Start)
|
||||
}
|
||||
pub const fn is_pause(&self) -> bool {
|
||||
matches!(*self, Self::Pause)
|
||||
}
|
||||
pub const fn is_stop(&self) -> bool {
|
||||
matches!(*self, Self::Stop)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for State {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
State::Open => write!(f, "open"),
|
||||
State::Start => write!(f, "start"),
|
||||
State::Stop => write!(f, "stop"),
|
||||
State::Pause => write!(f, "pause"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for State {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let result = match s.to_lowercase().as_str() {
|
||||
"open" => State::Open,
|
||||
"stop" => State::Stop,
|
||||
"start" => State::Start,
|
||||
"pause" => State::Pause,
|
||||
_ => return Err(Error::ParseFailed("unable to parse state")),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BaseTask {
|
||||
pub title: String,
|
||||
pub tags: Vec<String>,
|
||||
@@ -78,27 +32,10 @@ pub struct BaseTask {
|
||||
pub rank: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TaskInfo {
|
||||
pub ref_id: String,
|
||||
pub workspace: String,
|
||||
pub id: u32,
|
||||
pub title: String,
|
||||
pub tags: Vec<String>,
|
||||
pub desc: String,
|
||||
pub owner: String,
|
||||
pub assign: Vec<String>,
|
||||
pub project: Vec<String>,
|
||||
pub due: Option<u64>,
|
||||
pub rank: Option<f32>,
|
||||
pub created_at: u64,
|
||||
pub state: String,
|
||||
pub events: Vec<TaskEvent>,
|
||||
pub comments: Vec<Comment>,
|
||||
}
|
||||
|
||||
impl From<BaseTask> for TaskInfo {
|
||||
fn from(value: BaseTask) -> Self {
|
||||
let due = if let Some(vd) = value.due { Some(Timestamp(vd)) } else { None };
|
||||
|
||||
Self {
|
||||
ref_id: String::default(),
|
||||
workspace: String::default(),
|
||||
@@ -109,9 +46,9 @@ impl From<BaseTask> for TaskInfo {
|
||||
owner: String::default(),
|
||||
assign: value.assign,
|
||||
project: value.project,
|
||||
due: value.due,
|
||||
due,
|
||||
rank: value.rank,
|
||||
created_at: u64::default(),
|
||||
created_at: Timestamp(u64::default()),
|
||||
state: String::default(),
|
||||
events: vec![],
|
||||
comments: vec![],
|
||||
@@ -119,44 +56,6 @@ impl From<BaseTask> for TaskInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TaskEvent {
|
||||
pub action: String,
|
||||
pub author: String,
|
||||
pub content: String,
|
||||
pub timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TaskEvent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "action: {}, timestamp: {}", self.action, self.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TaskEvent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
action: State::Open.to_string(),
|
||||
author: "".to_string(),
|
||||
content: "".to_string(),
|
||||
timestamp: Timestamp::current_time(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Comment {
|
||||
content: String,
|
||||
author: String,
|
||||
timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Comment {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{} author: {}, content: {} ", self.timestamp, self.author, self.content)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn task_from_cli(values: Vec<String>) -> Result<BaseTask> {
|
||||
let mut title = String::new();
|
||||
let mut tags = vec![];
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use log::debug;
|
||||
use serde_json::{from_value, json};
|
||||
|
||||
use darkfi::{rpc::jsonrpc::JsonRequest, Result};
|
||||
use log::debug;
|
||||
use tinyjson::JsonValue;
|
||||
|
||||
use crate::{
|
||||
primitives::{BaseTask, State, TaskInfo},
|
||||
@@ -33,108 +32,172 @@ impl Tau {
|
||||
|
||||
/// Add a new task.
|
||||
pub async fn add(&self, task: BaseTask) -> Result<u32> {
|
||||
let req = JsonRequest::new("add", json!([task]));
|
||||
let mut params = vec![];
|
||||
params.push(JsonValue::String(task.title.clone()));
|
||||
params.push(JsonValue::Array(
|
||||
task.tags.iter().map(|x| JsonValue::String(x.clone())).collect(),
|
||||
));
|
||||
params.push(JsonValue::String(task.desc.unwrap_or("".to_string())));
|
||||
params.push(JsonValue::Array(
|
||||
task.assign.iter().map(|x| JsonValue::String(x.clone())).collect(),
|
||||
));
|
||||
params.push(JsonValue::Array(
|
||||
task.project.iter().map(|x| JsonValue::String(x.clone())).collect(),
|
||||
));
|
||||
|
||||
let due = if let Some(num) = task.due {
|
||||
JsonValue::String(num.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
};
|
||||
params.push(due);
|
||||
|
||||
let rank =
|
||||
if let Some(num) = task.rank { JsonValue::Number(num.into()) } else { JsonValue::Null };
|
||||
params.push(rank);
|
||||
|
||||
let req = JsonRequest::new("add", params);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
let reply: u32 = from_value(rep)?;
|
||||
Ok(reply)
|
||||
Ok(*rep.get::<f64>().unwrap() as u32)
|
||||
}
|
||||
|
||||
/// Get current open tasks ids.
|
||||
pub async fn get_ids(&self) -> Result<Vec<u64>> {
|
||||
let req = JsonRequest::new("get_ids", json!([]));
|
||||
pub async fn get_ids(&self) -> Result<Vec<u32>> {
|
||||
let req = JsonRequest::new("get_ids", vec![]);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
|
||||
let mut ret = vec![];
|
||||
for i in rep.as_array().unwrap() {
|
||||
ret.push(i.as_u64().unwrap());
|
||||
for i in rep.get::<Vec<JsonValue>>().unwrap() {
|
||||
ret.push(*i.get::<f64>().unwrap() as u32)
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Update existing task given it's ID and some params.
|
||||
pub async fn update(&self, id: u64, task: BaseTask) -> Result<bool> {
|
||||
let req = JsonRequest::new("update", json!([id, task]));
|
||||
pub async fn update(&self, id: u32, task: BaseTask) -> Result<bool> {
|
||||
let mut params = vec![];
|
||||
params.push(JsonValue::String(task.title.clone()));
|
||||
params.push(JsonValue::Array(
|
||||
task.tags.iter().map(|x| JsonValue::String(x.clone())).collect(),
|
||||
));
|
||||
params.push(JsonValue::String(task.desc.unwrap_or("".to_string())));
|
||||
params.push(JsonValue::Array(
|
||||
task.assign.iter().map(|x| JsonValue::String(x.clone())).collect(),
|
||||
));
|
||||
params.push(JsonValue::Array(
|
||||
task.project.iter().map(|x| JsonValue::String(x.clone())).collect(),
|
||||
));
|
||||
|
||||
let due = if let Some(num) = task.due {
|
||||
JsonValue::String(num.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
};
|
||||
params.push(due);
|
||||
|
||||
let rank =
|
||||
if let Some(num) = task.rank { JsonValue::Number(num.into()) } else { JsonValue::Null };
|
||||
params.push(rank);
|
||||
|
||||
let req = JsonRequest::new(
|
||||
"update",
|
||||
vec![JsonValue::Number(id.into()), JsonValue::Array(params)],
|
||||
);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
let reply: bool = from_value(rep)?;
|
||||
Ok(reply)
|
||||
Ok(*rep.get::<bool>().unwrap())
|
||||
}
|
||||
|
||||
/// Set the state for a task.
|
||||
pub async fn set_state(&self, id: u64, state: &State) -> Result<bool> {
|
||||
let req = JsonRequest::new("set_state", json!([id, state.to_string()]));
|
||||
pub async fn set_state(&self, id: u32, state: &State) -> Result<bool> {
|
||||
let req = JsonRequest::new(
|
||||
"set_state",
|
||||
vec![JsonValue::Number(id.into()), JsonValue::String(state.to_string())],
|
||||
);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
let reply: bool = from_value(rep)?;
|
||||
Ok(reply)
|
||||
Ok(*rep.get::<bool>().unwrap())
|
||||
}
|
||||
|
||||
/// Set a comment for a task.
|
||||
pub async fn set_comment(&self, id: u64, content: &str) -> Result<bool> {
|
||||
let req = JsonRequest::new("set_comment", json!([id, content]));
|
||||
pub async fn set_comment(&self, id: u32, content: &str) -> Result<bool> {
|
||||
let req = JsonRequest::new(
|
||||
"set_comment",
|
||||
vec![JsonValue::Number(id.into()), JsonValue::String(content.to_string())],
|
||||
);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
let reply: bool = from_value(rep)?;
|
||||
Ok(reply)
|
||||
Ok(*rep.get::<bool>().unwrap())
|
||||
}
|
||||
|
||||
/// Get task data by its ID.
|
||||
pub async fn get_task_by_id(&self, id: u64) -> Result<TaskInfo> {
|
||||
let req = JsonRequest::new("get_task_by_id", json!([id]));
|
||||
pub async fn get_task_by_id(&self, id: u32) -> Result<TaskInfo> {
|
||||
let req = JsonRequest::new("get_task_by_id", vec![JsonValue::Number(id.into())]);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
Ok(serde_json::from_value(rep)?)
|
||||
debug!("Got reply: {:?}", rep);
|
||||
let rep = rep.into();
|
||||
Ok(rep)
|
||||
}
|
||||
|
||||
/// Get month's stopped tasks.
|
||||
pub async fn get_stop_tasks(&self, month: Option<i64>) -> Result<Vec<TaskInfo>> {
|
||||
let req = JsonRequest::new("get_stop_tasks", json!([month]));
|
||||
pub async fn get_stop_tasks(&self, month: Option<u64>) -> Result<Vec<TaskInfo>> {
|
||||
let param = if let Some(month) = month {
|
||||
JsonValue::String(month.to_string())
|
||||
} else {
|
||||
JsonValue::Null
|
||||
};
|
||||
|
||||
let req = JsonRequest::new("get_stop_tasks", vec![param]);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
Ok(serde_json::from_value(rep)?)
|
||||
debug!("Got reply: {:?}", rep);
|
||||
let rep =
|
||||
rep.get::<Vec<JsonValue>>().unwrap().iter().map(|x| (*x).clone().into()).collect();
|
||||
Ok(rep)
|
||||
}
|
||||
|
||||
/// Switch workspace.
|
||||
pub async fn switch_ws(&self, workspace: String) -> Result<bool> {
|
||||
let req = JsonRequest::new("switch_ws", json!([workspace]));
|
||||
let req = JsonRequest::new("switch_ws", vec![JsonValue::String(workspace)]);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
let reply: bool = from_value(rep)?;
|
||||
Ok(reply)
|
||||
Ok(*rep.get::<bool>().unwrap())
|
||||
}
|
||||
|
||||
/// Get current workspace.
|
||||
pub async fn get_ws(&self) -> Result<String> {
|
||||
let req = JsonRequest::new("get_ws", json!([]));
|
||||
let req = JsonRequest::new("get_ws", vec![]);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
Ok(serde_json::from_value(rep)?)
|
||||
debug!("Got reply: {:?}", rep);
|
||||
Ok(rep.get::<String>().unwrap().clone())
|
||||
}
|
||||
|
||||
/// Export tasks.
|
||||
pub async fn export_to(&self, path: String) -> Result<bool> {
|
||||
let req = JsonRequest::new("export", json!([path]));
|
||||
let req = JsonRequest::new("export", vec![JsonValue::String(path)]);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
|
||||
Ok(serde_json::from_value(rep)?)
|
||||
Ok(*rep.get::<bool>().unwrap())
|
||||
}
|
||||
|
||||
/// Import tasks.
|
||||
pub async fn import_from(&self, path: String) -> Result<bool> {
|
||||
let req = JsonRequest::new("import", json!([path]));
|
||||
let req = JsonRequest::new("import", vec![JsonValue::String(path)]);
|
||||
let rep = self.rpc_client.request(req).await?;
|
||||
|
||||
debug!("Got reply: {:?}", rep);
|
||||
|
||||
Ok(serde_json::from_value(rep)?)
|
||||
Ok(*rep.get::<bool>().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,14 +90,18 @@ pub fn print_task_list(tasks: Vec<TaskInfo>, ws: String) -> Result<()> {
|
||||
print_tags.push(t)
|
||||
}
|
||||
|
||||
let due_ = match task.due {
|
||||
Some(ts) => ts.0,
|
||||
None => 0,
|
||||
};
|
||||
|
||||
table.add_row(Row::new(vec![
|
||||
Cell::new(&task.id.to_string()).style_spec(gen_style),
|
||||
Cell::new(&task.title).style_spec(gen_style),
|
||||
Cell::new(&print_tags.join(", ")).style_spec(gen_style),
|
||||
Cell::new(&task.project.join(", ")).style_spec(gen_style),
|
||||
Cell::new(&task.assign.join(", ")).style_spec(gen_style),
|
||||
Cell::new(×tamp_to_date(task.due.unwrap_or(0), DateFormat::Date))
|
||||
.style_spec(gen_style),
|
||||
Cell::new(×tamp_to_date(due_, DateFormat::Date)).style_spec(gen_style),
|
||||
if task.rank == max_rank {
|
||||
Cell::new(&rank).style_spec(max_style)
|
||||
} else if task.rank == min_rank {
|
||||
@@ -133,8 +137,13 @@ pub fn print_task_list(tasks: Vec<TaskInfo>, ws: String) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn taskinfo_table(taskinfo: TaskInfo) -> Result<Table> {
|
||||
let due = timestamp_to_date(taskinfo.due.unwrap_or(0), DateFormat::Date);
|
||||
let created_at = timestamp_to_date(taskinfo.created_at, DateFormat::DateTime);
|
||||
let due_ = match taskinfo.due {
|
||||
Some(ts) => ts.0,
|
||||
None => 0,
|
||||
};
|
||||
|
||||
let due = timestamp_to_date(due_, DateFormat::Date);
|
||||
let created_at = timestamp_to_date(taskinfo.created_at.0, DateFormat::DateTime);
|
||||
let rank = if let Some(r) = taskinfo.rank { r.to_string() } else { "".to_string() };
|
||||
|
||||
let mut table = table!(
|
||||
|
||||
@@ -60,7 +60,7 @@ pub fn to_json_result(res: TaudResult<JsonValue>, id: u16) -> JsonResult {
|
||||
TaudError::InvalidId => {
|
||||
JsonError::new(ErrorCode::InvalidParams, Some("invalid task id".into()), id).into()
|
||||
}
|
||||
TaudError::InvalidData(e) | TaudError::SerdeJsonError(e) => {
|
||||
TaudError::InvalidData(e) | TaudError::JsonError(e) => {
|
||||
JsonError::new(ErrorCode::InvalidParams, Some(e), id).into()
|
||||
}
|
||||
TaudError::InvalidDueTime => {
|
||||
|
||||
@@ -22,8 +22,7 @@ use async_std::sync::Mutex;
|
||||
use async_trait::async_trait;
|
||||
use crypto_box::ChaChaBox;
|
||||
use log::{debug, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use tinyjson::JsonValue;
|
||||
|
||||
use darkfi::{
|
||||
net,
|
||||
@@ -51,43 +50,25 @@ pub struct JsonRpcInterface {
|
||||
p2p: net::P2pPtr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
struct BaseTaskInfo {
|
||||
title: String,
|
||||
tags: Vec<String>,
|
||||
desc: String,
|
||||
assign: Vec<String>,
|
||||
project: Vec<String>,
|
||||
due: Option<Timestamp>,
|
||||
rank: Option<f32>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl RequestHandler for JsonRpcInterface {
|
||||
async fn handle_request(&self, req: JsonRequest) -> JsonResult {
|
||||
if !req.params.is_array() {
|
||||
return JsonError::new(ErrorCode::InvalidParams, None, req.id).into()
|
||||
}
|
||||
|
||||
let params = req.params.as_array().unwrap();
|
||||
|
||||
let rep = match req.method.as_str() {
|
||||
Some("add") => self.add(params).await,
|
||||
Some("get_ids") => self.get_ids(params).await,
|
||||
Some("update") => self.update(params).await,
|
||||
Some("set_state") => self.set_state(params).await,
|
||||
Some("set_comment") => self.set_comment(params).await,
|
||||
Some("get_task_by_id") => self.get_task_by_id(params).await,
|
||||
Some("switch_ws") => self.switch_ws(params).await,
|
||||
Some("get_ws") => self.get_ws(params).await,
|
||||
Some("export") => self.export_to(params).await,
|
||||
Some("import") => self.import_from(params).await,
|
||||
Some("get_stop_tasks") => self.get_stop_tasks(params).await,
|
||||
Some("ping") => self.pong(params).await,
|
||||
"add" => self.add(req.params).await,
|
||||
"get_ids" => self.get_ids(req.params).await,
|
||||
"update" => self.update(req.params).await,
|
||||
"set_state" => self.set_state(req.params).await,
|
||||
"set_comment" => self.set_comment(req.params).await,
|
||||
"get_task_by_id" => self.get_task_by_id(req.params).await,
|
||||
"switch_ws" => self.switch_ws(req.params).await,
|
||||
"get_ws" => self.get_ws(req.params).await,
|
||||
"export" => self.export_to(req.params).await,
|
||||
"import" => self.import_from(req.params).await,
|
||||
"get_stop_tasks" => self.get_stop_tasks(req.params).await,
|
||||
|
||||
Some("dnet_switch") => self.dnet_switch(params).await,
|
||||
Some("dnet_info") => self.dnet_info(params).await,
|
||||
Some(_) | None => return JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
|
||||
"ping" => return self.pong(req.id, req.params).await,
|
||||
"dnet_switch" => self.dnet_switch(req.params).await,
|
||||
_ => return JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
|
||||
};
|
||||
|
||||
to_json_result(rep, req.id)
|
||||
@@ -106,15 +87,6 @@ impl JsonRpcInterface {
|
||||
Self { dataset_path, nickname, workspace, workspaces, notify_queue_sender, p2p }
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Replies to a ping method.
|
||||
//
|
||||
// --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 42}
|
||||
// <-- {"jsonrpc": "2.0", "result": "pong", "id": 42}
|
||||
async fn pong(&self, _params: &[Value]) -> TaudResult<Value> {
|
||||
Ok(json!("pong"))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Activate or deactivate dnet in the P2P stack.
|
||||
// By sending `true`, dnet will be activated, and by sending `false` dnet will
|
||||
@@ -122,28 +94,21 @@ impl JsonRpcInterface {
|
||||
//
|
||||
// --> {"jsonrpc": "2.0", "method": "dnet_switch", "params": [true], "id": 42}
|
||||
// <-- {"jsonrpc": "2.0", "result": true, "id": 42}
|
||||
async fn dnet_switch(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
if params.len() != 1 && params[0].as_bool().is_none() {
|
||||
async fn dnet_switch(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
if params.len() != 1 || !params[0].is_bool() {
|
||||
return Err(TaudError::InvalidData("Invalid parameters".into()))
|
||||
}
|
||||
|
||||
if params[0].as_bool().unwrap() {
|
||||
let switch = params[0].get::<bool>().unwrap();
|
||||
|
||||
if *switch {
|
||||
self.p2p.dnet_enable().await;
|
||||
} else {
|
||||
self.p2p.dnet_disable().await;
|
||||
}
|
||||
|
||||
Ok(json!(true))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Retrieves P2P network information.
|
||||
//
|
||||
// --> {"jsonrpc": "2.0", "method": "dnet_info", "params": [], "id": 42}
|
||||
// <-- {"jsonrpc": "2.0", result": {"nodeID": [], "nodeinfo": [], "id": 42}
|
||||
async fn dnet_info(&self, _params: &[Value]) -> TaudResult<Value> {
|
||||
let dnet_info = self.p2p.dnet_info().await;
|
||||
Ok(net::P2p::map_dnet_info(dnet_info))
|
||||
Ok(JsonValue::Boolean(true))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
@@ -161,79 +126,156 @@ impl JsonRpcInterface {
|
||||
// "id": 1
|
||||
// }
|
||||
// <-- {"jsonrpc": "2.0", "result": true, "id": 1}
|
||||
async fn add(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn add(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::add() params {:?}", params);
|
||||
|
||||
let task: BaseTaskInfo = serde_json::from_value(params[0].clone())?;
|
||||
if params.len() != 7 ||
|
||||
!params[0].is_string() ||
|
||||
!params[1].is_array() ||
|
||||
!params[2].is_string() ||
|
||||
!params[3].is_array() ||
|
||||
!params[4].is_array()
|
||||
{
|
||||
return Err(TaudError::InvalidData("Invalid parameters".to_string()))
|
||||
}
|
||||
|
||||
let due = match ¶ms[5] {
|
||||
JsonValue::Null => None,
|
||||
JsonValue::String(u64_str) => match u64::from_str_radix(&u64_str, 10) {
|
||||
Ok(v) => Some(Timestamp(v)),
|
||||
Err(e) => return Err(TaudError::InvalidData(e.to_string())),
|
||||
},
|
||||
_ => return Err(TaudError::InvalidData("Invalid parameters".to_string())),
|
||||
};
|
||||
|
||||
let rank = match params[6] {
|
||||
JsonValue::Null => None,
|
||||
JsonValue::Number(numba) => Some(numba as f32),
|
||||
_ => return Err(TaudError::InvalidData("Invalid parameters".to_string())),
|
||||
};
|
||||
|
||||
let tags = {
|
||||
let mut tags = vec![];
|
||||
|
||||
for val in params[1].get::<Vec<JsonValue>>().unwrap().iter() {
|
||||
if let Some(tag) = val.get::<String>() {
|
||||
tags.push(tag.clone());
|
||||
} else {
|
||||
return Err(TaudError::InvalidData("Invalid parameters".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
tags
|
||||
};
|
||||
|
||||
let assigns = {
|
||||
let mut assigns = vec![];
|
||||
|
||||
for val in params[3].get::<Vec<JsonValue>>().unwrap().iter() {
|
||||
if let Some(assign) = val.get::<String>() {
|
||||
assigns.push(assign.clone());
|
||||
} else {
|
||||
return Err(TaudError::InvalidData("Invalid parameters".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
assigns
|
||||
};
|
||||
|
||||
let projects = {
|
||||
let mut projects = vec![];
|
||||
|
||||
for val in params[4].get::<Vec<JsonValue>>().unwrap().iter() {
|
||||
if let Some(project) = val.get::<String>() {
|
||||
projects.push(project.clone());
|
||||
} else {
|
||||
return Err(TaudError::InvalidData("Invalid parameters".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
projects
|
||||
};
|
||||
|
||||
let mut new_task: TaskInfo = TaskInfo::new(
|
||||
self.workspace.lock().await.clone(),
|
||||
&task.title,
|
||||
&task.desc,
|
||||
¶ms[0].get::<String>().unwrap(),
|
||||
¶ms[2].get::<String>().unwrap(),
|
||||
&self.nickname,
|
||||
task.due,
|
||||
task.rank,
|
||||
due,
|
||||
rank,
|
||||
&self.dataset_path,
|
||||
)?;
|
||||
new_task.set_project(&task.project);
|
||||
new_task.set_assign(&task.assign);
|
||||
new_task.set_tags(&task.tags);
|
||||
new_task.set_project(&projects);
|
||||
new_task.set_assign(&assigns);
|
||||
new_task.set_tags(&tags);
|
||||
|
||||
self.notify_queue_sender.send(new_task.clone()).await.map_err(Error::from)?;
|
||||
Ok(json!(new_task.id))
|
||||
Ok(JsonValue::Number(new_task.id.into()))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// List tasks
|
||||
// --> {"jsonrpc": "2.0", "method": "get_ids", "params": [], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": [task_id, ...], "id": 1}
|
||||
async fn get_ids(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn get_ids(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::get_ids() params {:?}", params);
|
||||
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
let tasks = MonthTasks::load_current_tasks(&self.dataset_path, ws, false)?;
|
||||
|
||||
let task_ids: Vec<u32> = tasks.iter().map(|task| task.get_id()).collect();
|
||||
let task_ids: Vec<JsonValue> =
|
||||
tasks.iter().map(|task| JsonValue::Number(task.get_id().into())).collect();
|
||||
|
||||
Ok(json!(task_ids))
|
||||
Ok(JsonValue::Array(task_ids))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Update task and returns `true` upon success.
|
||||
// --> {"jsonrpc": "2.0", "method": "update", "params": [task_id, {"title": "new title"} ], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": true, "id": 1}
|
||||
async fn update(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn update(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::update() params {:?}", params);
|
||||
|
||||
if params.len() != 2 {
|
||||
if params.len() != 2 || !params[0].is_number() || !params[1].is_object() {
|
||||
return Err(TaudError::InvalidData("len of params should be 2".into()))
|
||||
}
|
||||
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
let task = self.check_params_for_update(¶ms[0], ¶ms[1], ws)?;
|
||||
|
||||
let task = self.check_params_for_update(
|
||||
*params[0].get::<f64>().unwrap() as u32,
|
||||
params[1].get::<HashMap<String, JsonValue>>().unwrap(),
|
||||
ws,
|
||||
)?;
|
||||
|
||||
self.notify_queue_sender.send(task).await.map_err(Error::from)?;
|
||||
|
||||
Ok(json!(true))
|
||||
Ok(JsonValue::Boolean(true))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Set state for a task and returns `true` upon success.
|
||||
// --> {"jsonrpc": "2.0", "method": "set_state", "params": [task_id, state], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": true, "id": 1}
|
||||
async fn set_state(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn set_state(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
// Allowed states for a task
|
||||
let states = ["stop", "start", "open", "pause"];
|
||||
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::set_state() params {:?}", params);
|
||||
|
||||
if params.len() != 2 {
|
||||
if params.len() != 2 || !params[0].is_number() || !params[1].is_string() {
|
||||
return Err(TaudError::InvalidData("len of params should be 2".into()))
|
||||
}
|
||||
|
||||
let state: String = serde_json::from_value(params[1].clone())?;
|
||||
let state = params[1].get::<String>().unwrap();
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
|
||||
let mut task: TaskInfo = self.load_task_by_id(¶ms[0], ws)?;
|
||||
let mut task: TaskInfo =
|
||||
self.load_task_by_id(*params[0].get::<f64>().unwrap() as u32, ws)?;
|
||||
|
||||
if states.contains(&state.as_str()) {
|
||||
task.set_state(&state);
|
||||
@@ -242,73 +284,90 @@ impl JsonRpcInterface {
|
||||
|
||||
self.notify_queue_sender.send(task).await.map_err(Error::from)?;
|
||||
|
||||
Ok(json!(true))
|
||||
Ok(JsonValue::Boolean(true))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Set comment for a task and returns `true` upon success.
|
||||
// --> {"jsonrpc": "2.0", "method": "set_comment", "params": [task_id, comment_content], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": true, "id": 1}
|
||||
async fn set_comment(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn set_comment(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::set_comment() params {:?}", params);
|
||||
|
||||
if params.len() != 2 {
|
||||
if params.len() != 2 || !params[0].is_number() || !params[1].is_string() {
|
||||
return Err(TaudError::InvalidData("len of params should be 2".into()))
|
||||
}
|
||||
|
||||
let comment_content: String = serde_json::from_value(params[1].clone())?;
|
||||
let id = *params[0].get::<f64>().unwrap() as u32;
|
||||
let comment_content = params[1].get::<String>().unwrap();
|
||||
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
let mut task: TaskInfo = self.load_task_by_id(¶ms[0], ws)?;
|
||||
let mut task: TaskInfo = self.load_task_by_id(id, ws)?;
|
||||
|
||||
task.set_comment(Comment::new(&comment_content, &self.nickname));
|
||||
set_event(&mut task, "comment", &self.nickname, &comment_content);
|
||||
|
||||
self.notify_queue_sender.send(task).await.map_err(Error::from)?;
|
||||
|
||||
Ok(json!(true))
|
||||
Ok(JsonValue::Boolean(true))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Get a task by id.
|
||||
// --> {"jsonrpc": "2.0", "method": "get_task_by_id", "params": [task_id], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": "task", "id": 1}
|
||||
async fn get_task_by_id(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn get_task_by_id(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::get_task_by_id() params {:?}", params);
|
||||
|
||||
if params.len() != 1 {
|
||||
if params.len() != 1 || !params[0].is_number() {
|
||||
return Err(TaudError::InvalidData("len of params should be 1".into()))
|
||||
}
|
||||
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
let task: TaskInfo = self.load_task_by_id(¶ms[0], ws)?;
|
||||
let task: TaskInfo = self.load_task_by_id(*params[0].get::<f64>().unwrap() as u32, ws)?;
|
||||
let task: JsonValue = (&task).into();
|
||||
|
||||
Ok(json!(task))
|
||||
Ok(task)
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Get all tasks.
|
||||
// --> {"jsonrpc": "2.0", "method": "get_stop_tasks", "params": [task_id], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": "task", "id": 1}
|
||||
async fn get_stop_tasks(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn get_stop_tasks(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::get_stop_tasks() params {:?}", params);
|
||||
|
||||
if params.len() != 1 {
|
||||
if params.len() != 1 || !params[0].is_string() {
|
||||
return Err(TaudError::InvalidData("len of params should be 1".into()))
|
||||
}
|
||||
let month = params[0].as_u64().map(Timestamp);
|
||||
|
||||
let month = match params[0].get::<String>() {
|
||||
Some(u64_str) => match u64::from_str_radix(u64_str, 10) {
|
||||
Ok(v) => Some(Timestamp(v)),
|
||||
//Err(e) => return Err(TaudError::InvalidData(e.to_string())),
|
||||
Err(_) => None,
|
||||
},
|
||||
|
||||
None => None,
|
||||
};
|
||||
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
|
||||
let tasks = MonthTasks::load_stop_tasks(&self.dataset_path, ws, month.as_ref())?;
|
||||
let tasks: Vec<JsonValue> = tasks.iter().map(|x| x.into()).collect();
|
||||
|
||||
Ok(json!(tasks))
|
||||
Ok(JsonValue::Array(tasks))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Switch tasks workspace.
|
||||
// --> {"jsonrpc": "2.0", "method": "switch_ws", "params": [workspace], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": "true", "id": 1}
|
||||
async fn switch_ws(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn switch_ws(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::switch_ws() params {:?}", params);
|
||||
|
||||
if params.len() != 1 {
|
||||
@@ -319,34 +378,36 @@ impl JsonRpcInterface {
|
||||
return Err(TaudError::InvalidData("Invalid workspace".into()))
|
||||
}
|
||||
|
||||
let ws = params[0].as_str().unwrap().to_string();
|
||||
let ws = params[0].get::<String>().unwrap();
|
||||
let mut s = self.workspace.lock().await;
|
||||
|
||||
if self.workspaces.contains_key(&ws) {
|
||||
*s = ws
|
||||
if self.workspaces.contains_key(ws) {
|
||||
*s = ws.to_string()
|
||||
} else {
|
||||
warn!("Workspace \"{}\" is not configured", ws);
|
||||
return Ok(json!(false))
|
||||
return Ok(JsonValue::Boolean(false))
|
||||
}
|
||||
|
||||
Ok(json!(true))
|
||||
Ok(JsonValue::Boolean(true))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Get workspace.
|
||||
// --> {"jsonrpc": "2.0", "method": "get_ws", "params": [], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": "workspace", "id": 1}
|
||||
async fn get_ws(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn get_ws(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::get_ws() params {:?}", params);
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
Ok(json!(ws))
|
||||
Ok(JsonValue::String(ws))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Export tasks.
|
||||
// --> {"jsonrpc": "2.0", "method": "export_to", "params": [path], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": "true", "id": 1}
|
||||
async fn export_to(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn export_to(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::export_to() params {:?}", params);
|
||||
|
||||
if params.len() != 1 {
|
||||
@@ -358,7 +419,8 @@ impl JsonRpcInterface {
|
||||
}
|
||||
|
||||
// mkdir datastore_path if not exists
|
||||
let path = expand_path(params[0].as_str().unwrap())?.join("exported_tasks");
|
||||
let path = params[0].get::<String>().unwrap();
|
||||
let path = expand_path(path)?.join("exported_tasks");
|
||||
create_dir_all(path.join("month")).map_err(Error::from)?;
|
||||
create_dir_all(path.join("task")).map_err(Error::from)?;
|
||||
|
||||
@@ -369,14 +431,15 @@ impl JsonRpcInterface {
|
||||
task.save(&path)?;
|
||||
}
|
||||
|
||||
Ok(json!(true))
|
||||
Ok(JsonValue::Boolean(true))
|
||||
}
|
||||
|
||||
// RPCAPI:
|
||||
// Import tasks.
|
||||
// --> {"jsonrpc": "2.0", "method": "import_from", "params": [path], "id": 1}
|
||||
// <-- {"jsonrpc": "2.0", "result": "true", "id": 1}
|
||||
async fn import_from(&self, params: &[Value]) -> TaudResult<Value> {
|
||||
async fn import_from(&self, params: JsonValue) -> TaudResult<JsonValue> {
|
||||
let params = params.get::<Vec<JsonValue>>().unwrap();
|
||||
debug!(target: "tau", "JsonRpc::import_from() params {:?}", params);
|
||||
|
||||
if params.len() != 1 {
|
||||
@@ -387,7 +450,8 @@ impl JsonRpcInterface {
|
||||
return Err(TaudError::InvalidData("Invalid path".into()))
|
||||
}
|
||||
|
||||
let path = expand_path(params[0].as_str().unwrap())?.join("exported_tasks");
|
||||
let path = params[0].get::<String>().unwrap();
|
||||
let path = expand_path(path)?.join("exported_tasks");
|
||||
let ws = self.workspace.lock().await.clone();
|
||||
|
||||
let mut task_ids: Vec<u32> =
|
||||
@@ -411,34 +475,26 @@ impl JsonRpcInterface {
|
||||
task_ids.push(task.id);
|
||||
self.notify_queue_sender.send(task).await.map_err(Error::from)?;
|
||||
}
|
||||
Ok(json!(true))
|
||||
Ok(JsonValue::Boolean(true))
|
||||
}
|
||||
|
||||
fn load_task_by_id(&self, task_id: &Value, ws: String) -> TaudResult<TaskInfo> {
|
||||
let task_id: u64 = serde_json::from_value(task_id.clone())?;
|
||||
fn load_task_by_id(&self, task_id: u32, ws: String) -> TaudResult<TaskInfo> {
|
||||
let tasks = MonthTasks::load_current_tasks(&self.dataset_path, ws, false)?;
|
||||
let task = tasks.into_iter().find(|t| (t.get_id() as u64) == task_id);
|
||||
let task = tasks.into_iter().find(|t| (t.get_id()) == task_id);
|
||||
|
||||
task.ok_or(TaudError::InvalidId)
|
||||
}
|
||||
|
||||
fn check_params_for_update(
|
||||
&self,
|
||||
task_id: &Value,
|
||||
fields: &Value,
|
||||
task_id: u32,
|
||||
fields: &HashMap<String, JsonValue>,
|
||||
ws: String,
|
||||
) -> TaudResult<TaskInfo> {
|
||||
let mut task: TaskInfo = self.load_task_by_id(task_id, ws)?;
|
||||
|
||||
if !fields.is_object() {
|
||||
return Err(TaudError::InvalidData("Invalid task's data".into()))
|
||||
}
|
||||
|
||||
let fields = fields.as_object().unwrap();
|
||||
|
||||
if fields.contains_key("title") {
|
||||
let title = fields.get("title").unwrap().clone();
|
||||
let title: String = serde_json::from_value(title)?;
|
||||
let title = fields["title"].get::<String>().unwrap();
|
||||
if !title.is_empty() {
|
||||
task.set_title(&title);
|
||||
set_event(&mut task, "title", &self.nickname, &title);
|
||||
@@ -446,19 +502,23 @@ impl JsonRpcInterface {
|
||||
}
|
||||
|
||||
if fields.contains_key("desc") {
|
||||
let description = fields.get("desc");
|
||||
if let Some(description) = description {
|
||||
let description: Option<String> = serde_json::from_value(description.clone())?;
|
||||
if let Some(desc) = description {
|
||||
task.set_desc(&desc);
|
||||
set_event(&mut task, "desc", &self.nickname, &desc);
|
||||
}
|
||||
let desc = fields["desc"].get::<String>().unwrap();
|
||||
if !desc.is_empty() {
|
||||
task.set_desc(&desc);
|
||||
set_event(&mut task, "desc", &self.nickname, &desc);
|
||||
}
|
||||
}
|
||||
|
||||
if fields.contains_key("rank") {
|
||||
let rank_opt = fields.get("rank").unwrap();
|
||||
let rank: Option<Option<f32>> = serde_json::from_value(rank_opt.clone())?;
|
||||
// TODO: Why is this a double Option?
|
||||
let rank = {
|
||||
match fields["rank"] {
|
||||
JsonValue::Null => None,
|
||||
JsonValue::Number(rank) => Some(Some(rank as f32)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(rank) = rank {
|
||||
task.set_rank(rank);
|
||||
match rank {
|
||||
@@ -473,8 +533,17 @@ impl JsonRpcInterface {
|
||||
}
|
||||
|
||||
if fields.contains_key("due") {
|
||||
let due = fields.get("due").unwrap().clone();
|
||||
let due: Option<Option<Timestamp>> = serde_json::from_value(due)?;
|
||||
// TODO: Why is this a double Option?
|
||||
let due = {
|
||||
match &fields["due"] {
|
||||
JsonValue::Null => None,
|
||||
JsonValue::String(ts_str) => {
|
||||
Some(Some(Timestamp(u64::from_str_radix(&ts_str, 10).unwrap())))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(d) = due {
|
||||
task.set_due(d);
|
||||
match d {
|
||||
@@ -489,8 +558,13 @@ impl JsonRpcInterface {
|
||||
}
|
||||
|
||||
if fields.contains_key("assign") {
|
||||
let assign = fields.get("assign").unwrap().clone();
|
||||
let assign: Vec<String> = serde_json::from_value(assign)?;
|
||||
let assign: Vec<String> = fields["assign"]
|
||||
.get::<Vec<JsonValue>>()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x.get::<String>().unwrap().clone())
|
||||
.collect();
|
||||
|
||||
if !assign.is_empty() {
|
||||
task.set_assign(&assign);
|
||||
set_event(&mut task, "assign", &self.nickname, &assign.join(", "));
|
||||
@@ -498,8 +572,13 @@ impl JsonRpcInterface {
|
||||
}
|
||||
|
||||
if fields.contains_key("project") {
|
||||
let project = fields.get("project").unwrap().clone();
|
||||
let project: Vec<String> = serde_json::from_value(project)?;
|
||||
let project: Vec<String> = fields["project"]
|
||||
.get::<Vec<JsonValue>>()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x.get::<String>().unwrap().clone())
|
||||
.collect();
|
||||
|
||||
if !project.is_empty() {
|
||||
task.set_project(&project);
|
||||
set_event(&mut task, "project", &self.nickname, &project.join(", "));
|
||||
@@ -507,8 +586,13 @@ impl JsonRpcInterface {
|
||||
}
|
||||
|
||||
if fields.contains_key("tags") {
|
||||
let tags = fields.get("tags").unwrap().clone();
|
||||
let tags: Vec<String> = serde_json::from_value(tags)?;
|
||||
let tags: Vec<String> = fields["tags"]
|
||||
.get::<Vec<JsonValue>>()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x.get::<String>().unwrap().clone())
|
||||
.collect();
|
||||
|
||||
if !tags.is_empty() {
|
||||
task.set_tags(&tags);
|
||||
set_event(&mut task, "tags", &self.nickname, &tags.join(", "));
|
||||
|
||||
@@ -39,6 +39,7 @@ use futures::{select, FutureExt};
|
||||
use log::{debug, error, info};
|
||||
use rand::rngs::OsRng;
|
||||
use structopt_toml::StructOptToml;
|
||||
use tinyjson::JsonValue;
|
||||
|
||||
use darkfi::{
|
||||
async_daemonize,
|
||||
@@ -218,31 +219,31 @@ async fn on_receive_task(
|
||||
// otherwise it's a modification.
|
||||
match TaskInfo::load(&task.ref_id, datastore_path) {
|
||||
Ok(loaded_task) => {
|
||||
let loaded_events = loaded_task.events.0;
|
||||
let mut events = task.events.0.clone();
|
||||
let loaded_events = loaded_task.events;
|
||||
let mut events = task.events.clone();
|
||||
events.retain(|ev| !loaded_events.contains(ev));
|
||||
|
||||
let file = "/tmp/tau_pipe";
|
||||
let mut pipe_write = pipe_write(file)?;
|
||||
let mut task_clone = task.clone();
|
||||
task_clone.events.0 = events;
|
||||
task_clone.events = events;
|
||||
|
||||
let json = serde_json::to_string(&task_clone).unwrap();
|
||||
pipe_write.write_all(json.as_bytes())?;
|
||||
let json: JsonValue = (&task_clone).into();
|
||||
pipe_write.write_all(json.stringify().unwrap().as_bytes())?;
|
||||
}
|
||||
Err(_) => {
|
||||
let file = "/tmp/tau_pipe";
|
||||
let mut pipe_write = pipe_write(file)?;
|
||||
let mut task_clone = task.clone();
|
||||
|
||||
task_clone.events.0.push(TaskEvent::new(
|
||||
task_clone.events.push(TaskEvent::new(
|
||||
"add_task".to_string(),
|
||||
task_clone.owner.clone(),
|
||||
"".to_string(),
|
||||
));
|
||||
|
||||
let json = serde_json::to_string(&task_clone).unwrap();
|
||||
pipe_write.write_all(json.as_bytes())?;
|
||||
let json: JsonValue = (&task_clone).into();
|
||||
pipe_write.write_all(json.stringify().unwrap().as_bytes())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use darkfi_serial::{SerialDecodable, SerialEncodable};
|
||||
use log::debug;
|
||||
@@ -28,6 +33,7 @@ use darkfi::{
|
||||
file::{load_json_file, save_json_file},
|
||||
time::Timestamp,
|
||||
},
|
||||
Error,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -36,6 +42,51 @@ use crate::{
|
||||
util::find_free_id,
|
||||
};
|
||||
|
||||
pub enum State {
|
||||
Open,
|
||||
Start,
|
||||
Pause,
|
||||
Stop,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn is_start(&self) -> bool {
|
||||
matches!(*self, Self::Start)
|
||||
}
|
||||
pub const fn is_pause(&self) -> bool {
|
||||
matches!(*self, Self::Pause)
|
||||
}
|
||||
pub const fn is_stop(&self) -> bool {
|
||||
matches!(*self, Self::Stop)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for State {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
State::Open => write!(f, "open"),
|
||||
State::Start => write!(f, "start"),
|
||||
State::Stop => write!(f, "stop"),
|
||||
State::Pause => write!(f, "pause"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for State {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let result = match s.to_lowercase().as_str() {
|
||||
"open" => State::Open,
|
||||
"stop" => State::Stop,
|
||||
"start" => State::Start,
|
||||
"pause" => State::Pause,
|
||||
_ => return Err(Error::ParseFailed("unable to parse state")),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerialEncodable, SerialDecodable, PartialEq, Eq)]
|
||||
pub struct TaskEvent {
|
||||
pub action: String,
|
||||
@@ -44,6 +95,23 @@ pub struct TaskEvent {
|
||||
pub timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TaskEvent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "action: {}, timestamp: {}", self.action, self.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TaskEvent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
action: State::Open.to_string(),
|
||||
author: "".to_string(),
|
||||
content: "".to_string(),
|
||||
timestamp: Timestamp::current_time(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskEvent {
|
||||
pub fn new(action: String, author: String, content: String) -> Self {
|
||||
Self { action, author, content, timestamp: Timestamp::current_time() }
|
||||
@@ -65,9 +133,9 @@ impl From<&JsonValue> for TaskEvent {
|
||||
fn from(value: &JsonValue) -> TaskEvent {
|
||||
let map = value.get::<HashMap<String, JsonValue>>().unwrap();
|
||||
TaskEvent {
|
||||
action: map["action"].get().unwrap().clone(),
|
||||
author: map["author"].get().unwrap().clone(),
|
||||
content: map["content"].get().unwrap().clone(),
|
||||
action: map["action"].get::<String>().unwrap().clone(),
|
||||
author: map["author"].get::<String>().unwrap().clone(),
|
||||
content: map["content"].get::<String>().unwrap().clone(),
|
||||
timestamp: Timestamp(
|
||||
u64::from_str_radix(map["timestamp"].get::<String>().unwrap(), 10).unwrap(),
|
||||
),
|
||||
@@ -82,6 +150,12 @@ pub struct Comment {
|
||||
timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Comment {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{} author: {}, content: {} ", self.timestamp, self.author, self.content)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Comment> for JsonValue {
|
||||
fn from(comment: Comment) -> JsonValue {
|
||||
JsonValue::Object(HashMap::from([
|
||||
@@ -96,8 +170,8 @@ impl From<JsonValue> for Comment {
|
||||
fn from(value: JsonValue) -> Comment {
|
||||
let map = value.get::<HashMap<String, JsonValue>>().unwrap();
|
||||
Comment {
|
||||
content: map["content"].get().unwrap().clone(),
|
||||
author: map["author"].get().unwrap().clone(),
|
||||
content: map["content"].get::<String>().unwrap().clone(),
|
||||
author: map["author"].get::<String>().unwrap().clone(),
|
||||
timestamp: Timestamp(
|
||||
u64::from_str_radix(map["timestamp"].get::<String>().unwrap(), 10).unwrap(),
|
||||
),
|
||||
@@ -218,7 +292,7 @@ impl From<JsonValue> for TaskInfo {
|
||||
};
|
||||
|
||||
let events: Vec<TaskEvent> = events.iter().map(|x| x.into()).collect();
|
||||
let comments: Vec<Comment> = comments.iter().map(|x| (*x).into()).collect();
|
||||
let comments: Vec<Comment> = comments.iter().map(|x| (*x).clone().into()).collect();
|
||||
|
||||
TaskInfo {
|
||||
ref_id: value["ref_id"].get::<String>().unwrap().clone(),
|
||||
|
||||
@@ -155,9 +155,13 @@ pub struct JsonRequest {
|
||||
impl JsonRequest {
|
||||
/// Create a new [`JsonRequest`] object with the given method and parameters.
|
||||
/// The request ID is chosen randomly.
|
||||
pub fn new(method: &str, params: JsonValue) -> Self {
|
||||
assert!(params.is_array());
|
||||
Self { jsonrpc: "2.0", id: OsRng::gen(&mut OsRng), method: method.to_string(), params }
|
||||
pub fn new(method: &str, params: Vec<JsonValue>) -> Self {
|
||||
Self {
|
||||
jsonrpc: "2.0",
|
||||
id: OsRng::gen(&mut OsRng),
|
||||
method: method.to_string(),
|
||||
params: JsonValue::Array(params),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the object into a JSON string
|
||||
|
||||
Reference in New Issue
Block a user