summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Unterwaditzer <markus@unterwaditzer.net>2015-01-15 15:17:51 +0100
committerMarkus Unterwaditzer <markus@unterwaditzer.net>2015-01-15 15:23:55 +0100
commit24e977e9d9ccee34b89dfbdb7ace386afc27ed2a (patch)
tree610c27cb676521c11f3bba60a39a9bd165546fe6
parentdb0834718f1e487b37cbb795cdc135e19c1defd5 (diff)
Ha, API rewrite
-rw-r--r--src/vobject/lib.rs102
-rw-r--r--tests/lib.rs48
2 files changed, 98 insertions, 52 deletions
diff --git a/src/vobject/lib.rs b/src/vobject/lib.rs
index 861289a..695e09b 100644
--- a/src/vobject/lib.rs
+++ b/src/vobject/lib.rs
@@ -10,9 +10,15 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
pub struct Property {
- params: HashMap<String, String>,
- raw_value: String,
- prop_group: Option<String>
+ /// Parameters.
+ pub params: HashMap<String, String>,
+
+ /// Value as unparsed string.
+ pub raw_value: String,
+
+ /// Property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
+ /// `"foo"`.
+ pub prop_group: Option<String>
}
impl Property {
@@ -24,25 +30,9 @@ impl Property {
}
}
- /// Get property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
- /// `"foo"`.
- pub fn get_prop_group(&self) -> &Option<String> {
- &self.prop_group
- }
-
- /// Get parameters.
- pub fn get_params(&self) -> &HashMap<String, String> {
- &self.params
- }
-
- /// Get value as unparsed string.
- pub fn get_raw_value(&self) -> &String {
- &self.raw_value
- }
-
/// Get value as unescaped string.
pub fn value_as_string(&self) -> String {
- unescape_chars(self.get_raw_value())
+ unescape_chars(self.raw_value.as_slice())
}
}
@@ -206,19 +196,54 @@ __ = (eol / whitespace)*
/// Parse a component. The error value is a human-readable message.
-pub fn parse_component(s: &String) -> Result<Component, String> {
+pub fn parse_component(s: &str) -> Result<Component, String> {
// XXX: The unfolding should be worked into the PEG
// See feature request: https://github.com/kevinmehall/rust-peg/issues/26
- let unfolded = s
- .replace("\r\n ", "").replace("\r\n\t", "")
- .replace("\n ", "").replace("\n\t", "")
- .replace("\r ", "").replace("\r\t", "");
-
+ let unfolded = unfold_lines(s);
parser::component(unfolded.as_slice())
}
+/// Write a component. The error value is a human-readable message.
+pub fn write_component(c: &Component) -> String {
+ fn inner(buf: &mut String, c: &Component) {
+ buf.push_str("BEGIN:");
+ buf.push_str(c.name.as_slice());
+ buf.push_str("\r\n");
+
+ for (prop_name, props) in c.props.iter() {
+ for prop in props.iter() {
+ match prop.prop_group {
+ Some(ref x) => { buf.push_str(x.as_slice()); buf.push('.'); },
+ None => ()
+ };
+ buf.push_str(prop_name.as_slice());
+ for (param_key, param_value) in prop.params.iter() {
+ buf.push(';');
+ buf.push_str(param_key.as_slice());
+ buf.push('=');
+ buf.push_str(param_value.as_slice());
+ };
+ buf.push(':');
+ buf.push_str(fold_line(prop.raw_value.as_slice()).as_slice());
+ };
+ };
+
+ for subcomponent in c.subcomponents.iter() {
+ inner(buf, subcomponent);
+ };
+
+ buf.push_str("END:");
+ buf.push_str(c.name.as_slice());
+ buf.push_str("\r\n");
+ }
+
+ let mut buf = String::new();
+ inner(&mut buf, c);
+ buf
+}
+
/// Escape text for a VObject property value.
-pub fn escape_chars(s: &String) -> String {
+pub fn escape_chars(s: &str) -> String {
// Order matters! Lifted from icalendar.parser
// https://github.com/collective/icalendar/
s
@@ -231,7 +256,7 @@ pub fn escape_chars(s: &String) -> String {
}
/// Unescape text from a VObject property value.
-pub fn unescape_chars(s: &String) -> String {
+pub fn unescape_chars(s: &str) -> String {
// Order matters! Lifted from icalendar.parser
// https://github.com/collective/icalendar/
s
@@ -242,3 +267,24 @@ pub fn unescape_chars(s: &String) -> String {
.replace("\\;", ";")
.replace("\\\\", "\\")
}
+
+/// Unfold contentline.
+pub fn unfold_lines(s: &str) -> String {
+ s
+ .replace("\r\n ", "").replace("\r\n\t", "")
+ .replace("\n ", "").replace("\n\t", "")
+ .replace("\r ", "").replace("\r\t", "")
+}
+
+/// Fold contentline to 75 chars. This function assumes the input to be unfolded, which means no
+/// '\n' or '\r' in it.
+pub fn fold_line(s: &str) -> String {
+ let mut rv = String::new();
+ for (i, c) in s.chars().enumerate() {
+ rv.push(c);
+ if i % 75 == 0 {
+ rv.push_str("\r\n ");
+ };
+ };
+ rv
+}
diff --git a/tests/lib.rs b/tests/lib.rs
index 72496fe..01efced 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -1,16 +1,16 @@
-#![feature(globs,macro_rules)]
+#![allow(unstable)]
extern crate vobject;
use vobject::parse_component;
use std::borrow::ToOwned;
macro_rules! s(
- ($i:expr) => (&$i.to_owned());
+ ($i:expr) => ($i.to_owned());
);
#[test]
fn test_vcard_basic() {
- let item = parse_component(s!(
+ let item = parse_component(
"BEGIN:VCARD\n\
VERSION:2.1\n\
N:Mustermann;Erika\n\
@@ -23,12 +23,12 @@ fn test_vcard_basic() {
ADR;HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\n\
EMAIL;PREF;INTERNET:erika@mustermann.de\n\
REV:20140301T221110Z\n\
- END:VCARD\n\r\n\n")).unwrap();
+ END:VCARD\n\r\n\n").unwrap();
- assert_eq!(item.single_prop("FN").unwrap().get_raw_value(), s!("Erika Mustermann"));
- assert_eq!(item.single_prop("N").unwrap().get_raw_value(), s!("Mustermann;Erika"));
+ assert_eq!(item.single_prop("FN").unwrap().raw_value, s!("Erika Mustermann"));
+ assert_eq!(item.single_prop("N").unwrap().raw_value, s!("Mustermann;Erika"));
- let mut tel_values = item.all_props("TEL").iter().map(|x| x.get_raw_value());
+ let mut tel_values = item.all_props("TEL").iter().map(|x| x.raw_value.as_slice());
assert_eq!(tel_values.next().unwrap(), s!("(0221) 9999123"));
assert_eq!(tel_values.next().unwrap(), s!("(0221) 1234567"));
assert!(tel_values.next().is_none());
@@ -36,7 +36,7 @@ fn test_vcard_basic() {
#[test]
fn test_line_cont() {
- let item = parse_component(s!(
+ let item = parse_component(
"BEGIN:VCARD\n\
VERSION:2.1\n\
N;ENCODING=QUOTED-PRINTABLE:Nikdo;Nikdo=\n\t\
@@ -44,16 +44,16 @@ fn test_line_cont() {
NOTE:This ends with equal sign=\n\
TEL;WORK:5555\n \
4444\n\
- END:VCARD")).unwrap();
+ END:VCARD").unwrap();
- assert_eq!(&item.name, s!("VCARD"));
- assert_eq!(item.single_prop("TEL").unwrap().get_raw_value(), s!("55554444"));
- assert_eq!(item.single_prop("N").unwrap().get_raw_value(), s!("Nikdo;Nikdo=vic"));
+ assert_eq!(item.name, s!("VCARD"));
+ assert_eq!(item.single_prop("TEL").unwrap().raw_value, s!("55554444"));
+ assert_eq!(item.single_prop("N").unwrap().raw_value, s!("Nikdo;Nikdo=vic"));
}
#[test]
fn test_icalendar_basic() {
- let item = parse_component(s!(
+ let item = parse_component(
"BEGIN:VCALENDAR\n\
VERSION:2.0\n\
PRODID:http://www.example.com/calendarapplication/\n\
@@ -69,35 +69,35 @@ fn test_icalendar_basic() {
DTEND:20060919T215900Z\n\
DTSTAMP:20060812T125900Z\n\
END:VEVENT\n\
- END:VCALENDAR\n")).unwrap();
+ END:VCALENDAR\n").unwrap();
- assert_eq!(&item.name, s!("VCALENDAR"));
+ assert_eq!(item.name, s!("VCALENDAR"));
assert!(item.single_prop("LOCATION").is_none());
assert!(item.single_prop("ORGANIZER").is_none());
let event = &item.subcomponents[0];
- assert_eq!(&event.name, s!("VEVENT"));
+ assert_eq!(event.name, s!("VEVENT"));
assert!(event.single_prop("ORGANIZER").is_some());
- assert_eq!(event.single_prop("LOCATION").unwrap().get_raw_value(), s!("Somewhere"));
+ assert_eq!(event.single_prop("LOCATION").unwrap().raw_value, s!("Somewhere"));
}
#[test]
fn test_escaping() {
- let item = parse_component(s!(
+ let item = parse_component(
"BEGIN:VCALENDAR\n\
ORGANIZER;CN=\"Cott:n Eye Joe\":mailto:joe@joe.com\n\
- END:VCALENDAR\n")).unwrap();
- assert_eq!(&item.name, s!("VCALENDAR"));
- assert_eq!(item.single_prop("ORGANIZER").unwrap().get_raw_value(), s!("mailto:joe@joe.com"));
+ END:VCALENDAR\n").unwrap();
+ assert_eq!(item.name, s!("VCALENDAR"));
+ assert_eq!(item.single_prop("ORGANIZER").unwrap().raw_value, s!("mailto:joe@joe.com"));
}
#[test]
fn test_property_groups() {
- let item = parse_component(s!(
+ let item = parse_component(
"BEGIN:VCARD\n\
foo.EMAIL;TYPE=INTERNET:foo@example.com\n\
foo.X-ACTUAL-TYPE:CUSTOM\n\
- END:VCARD\n")).unwrap();
- assert_eq!(item.single_prop("EMAIL").unwrap().get_prop_group(), &Some("foo".to_owned()));
+ END:VCARD\n").unwrap();
+ assert_eq!(item.single_prop("EMAIL").unwrap().prop_group, Some("foo".to_owned()));
}