Unhandled lines from iCal files are stored
This commit is contained in:
parent
49bdb3d199
commit
f7acadc3e2
3 changed files with 62 additions and 25 deletions
|
@ -5,7 +5,11 @@ use std::error::Error;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use ics::properties::{Completed, Created, LastModified, PercentComplete, Status, Summary};
|
use ics::properties::{Completed, Created, LastModified, PercentComplete, Status, Summary};
|
||||||
use ics::{ICalendar, ToDo};
|
use ics::{ICalendar, ToDo};
|
||||||
|
use ics::components::Parameter as IcsParameter;
|
||||||
|
use ics::components::Property as IcsProperty;
|
||||||
|
use ical::property::Property as IcalProperty;
|
||||||
|
|
||||||
|
use crate::Task;
|
||||||
use crate::item::Item;
|
use crate::item::Item;
|
||||||
use crate::task::CompletionStatus;
|
use crate::task::CompletionStatus;
|
||||||
use crate::settings::{ORG_NAME, PRODUCT_NAME};
|
use crate::settings::{ORG_NAME, PRODUCT_NAME};
|
||||||
|
@ -16,22 +20,27 @@ fn ical_product_id() -> String {
|
||||||
|
|
||||||
/// Create an iCal item from a `crate::item::Item`
|
/// Create an iCal item from a `crate::item::Item`
|
||||||
pub fn build_from(item: &Item) -> Result<String, Box<dyn Error>> {
|
pub fn build_from(item: &Item) -> Result<String, Box<dyn Error>> {
|
||||||
let s_last_modified = format_date_time(item.last_modified());
|
match item {
|
||||||
|
Item::Task(t) => build_from_task(t),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_from_task(task: &Task) -> Result<String, Box<dyn Error>> {
|
||||||
|
let s_last_modified = format_date_time(task.last_modified());
|
||||||
|
|
||||||
let mut todo = ToDo::new(
|
let mut todo = ToDo::new(
|
||||||
item.uid(),
|
task.uid(),
|
||||||
s_last_modified.clone(),
|
s_last_modified.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
item.creation_date().map(|dt|
|
task.creation_date().map(|dt|
|
||||||
todo.push(Created::new(format_date_time(dt)))
|
todo.push(Created::new(format_date_time(dt)))
|
||||||
);
|
);
|
||||||
todo.push(LastModified::new(s_last_modified));
|
todo.push(LastModified::new(s_last_modified));
|
||||||
todo.push(Summary::new(item.name()));
|
todo.push(Summary::new(task.name()));
|
||||||
|
|
||||||
match item {
|
match task.completion_status() {
|
||||||
Item::Task(t) => {
|
|
||||||
match t.completion_status() {
|
|
||||||
CompletionStatus::Uncompleted => {
|
CompletionStatus::Uncompleted => {
|
||||||
todo.push(Status::needs_action());
|
todo.push(Status::needs_action());
|
||||||
},
|
},
|
||||||
|
@ -43,10 +52,11 @@ pub fn build_from(item: &Item) -> Result<String, Box<dyn Error>> {
|
||||||
todo.push(Status::completed());
|
todo.push(Status::completed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ => {
|
// Also add fields that we have not handled
|
||||||
unimplemented!()
|
for ical_property in task.extra_parameters() {
|
||||||
},
|
let ics_property = ical_to_ics_property(ical_property.clone());
|
||||||
|
todo.push(ics_property);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut calendar = ICalendar::new("2.0", ical_product_id());
|
let mut calendar = ICalendar::new("2.0", ical_product_id());
|
||||||
|
@ -60,6 +70,21 @@ fn format_date_time(dt: &DateTime<Utc>) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn ical_to_ics_property(prop: IcalProperty) -> IcsProperty<'static> {
|
||||||
|
let mut ics_prop = match prop.value {
|
||||||
|
Some(value) => IcsProperty::new(prop.name, value),
|
||||||
|
None => IcsProperty::new(prop.name, ""),
|
||||||
|
};
|
||||||
|
prop.params.map(|v| {
|
||||||
|
for (key, vec_values) in v {
|
||||||
|
let values = vec_values.join(";");
|
||||||
|
ics_prop.add(IcsParameter::new(key, values));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ics_prop
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -36,6 +36,8 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result<
|
||||||
let mut last_modified = None;
|
let mut last_modified = None;
|
||||||
let mut completion_date = None;
|
let mut completion_date = None;
|
||||||
let mut creation_date = None;
|
let mut creation_date = None;
|
||||||
|
let mut extra_parameters = Vec::new();
|
||||||
|
|
||||||
for prop in &todo.properties {
|
for prop in &todo.properties {
|
||||||
match prop.name.as_str() {
|
match prop.name.as_str() {
|
||||||
"SUMMARY" => { name = prop.value.clone() },
|
"SUMMARY" => { name = prop.value.clone() },
|
||||||
|
@ -67,7 +69,8 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// This field is not supported.
|
// This field is not supported. Let's store it anyway, so that we are able to re-create an identical iCal file
|
||||||
|
extra_parameters.push(prop.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +96,7 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result<
|
||||||
true => CompletionStatus::Completed(completion_date),
|
true => CompletionStatus::Completed(completion_date),
|
||||||
};
|
};
|
||||||
|
|
||||||
Item::Task(Task::new_with_parameters(name, uid, item_id, completion_status, sync_status, creation_date, last_modified))
|
Item::Task(Task::new_with_parameters(name, uid, item_id, completion_status, sync_status, creation_date, last_modified, extra_parameters))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
13
src/task.rs
13
src/task.rs
|
@ -3,6 +3,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use ical::property::Property;
|
||||||
|
|
||||||
use crate::item::ItemId;
|
use crate::item::ItemId;
|
||||||
use crate::item::SyncStatus;
|
use crate::item::SyncStatus;
|
||||||
|
@ -51,6 +52,9 @@ pub struct Task {
|
||||||
/// The display name of the task
|
/// The display name of the task
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
|
/// Extra parameters that have not been parsed from the iCal file (because they're not supported (yet) by this crate).
|
||||||
|
/// They are needed to serialize this item into an equivalent iCal file
|
||||||
|
extra_parameters: Vec<Property>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,13 +70,16 @@ impl Task {
|
||||||
let new_completion_status = if completed {
|
let new_completion_status = if completed {
|
||||||
CompletionStatus::Completed(Some(Utc::now()))
|
CompletionStatus::Completed(Some(Utc::now()))
|
||||||
} else { CompletionStatus::Uncompleted };
|
} else { CompletionStatus::Uncompleted };
|
||||||
Self::new_with_parameters(name, new_uid, new_item_id, new_completion_status, new_sync_status, new_creation_date, new_last_modified)
|
let extra_parameters = Vec::new();
|
||||||
|
Self::new_with_parameters(name, new_uid, new_item_id, new_completion_status, new_sync_status, new_creation_date, new_last_modified, extra_parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Task instance, that may be synced on the server already
|
/// Create a new Task instance, that may be synced on the server already
|
||||||
pub fn new_with_parameters(name: String, uid: String, id: ItemId,
|
pub fn new_with_parameters(name: String, uid: String, id: ItemId,
|
||||||
completion_status: CompletionStatus,
|
completion_status: CompletionStatus,
|
||||||
sync_status: SyncStatus, creation_date: Option<DateTime<Utc>>, last_modified: DateTime<Utc>) -> Self
|
sync_status: SyncStatus, creation_date: Option<DateTime<Utc>>, last_modified: DateTime<Utc>,
|
||||||
|
extra_parameters: Vec<Property>,
|
||||||
|
) -> Self
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
@ -82,6 +89,7 @@ impl Task {
|
||||||
sync_status,
|
sync_status,
|
||||||
creation_date,
|
creation_date,
|
||||||
last_modified,
|
last_modified,
|
||||||
|
extra_parameters,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +101,7 @@ impl Task {
|
||||||
pub fn last_modified(&self) -> &DateTime<Utc> { &self.last_modified }
|
pub fn last_modified(&self) -> &DateTime<Utc> { &self.last_modified }
|
||||||
pub fn creation_date(&self) -> Option<&DateTime<Utc>> { self.creation_date.as_ref() }
|
pub fn creation_date(&self) -> Option<&DateTime<Utc>> { self.creation_date.as_ref() }
|
||||||
pub fn completion_status(&self) -> &CompletionStatus { &self.completion_status }
|
pub fn completion_status(&self) -> &CompletionStatus { &self.completion_status }
|
||||||
|
pub fn extra_parameters(&self) -> &[Property] { &self.extra_parameters }
|
||||||
|
|
||||||
#[cfg(any(test, feature = "integration_tests"))]
|
#[cfg(any(test, feature = "integration_tests"))]
|
||||||
pub fn has_same_observable_content_as(&self, other: &Task) -> bool {
|
pub fn has_same_observable_content_as(&self, other: &Task) -> bool {
|
||||||
|
|
Loading…
Add table
Reference in a new issue