diff options
author | Markus Unterwaditzer <markus@unterwaditzer.net> | 2015-01-15 15:17:51 +0100 |
---|---|---|
committer | Markus Unterwaditzer <markus@unterwaditzer.net> | 2015-01-15 15:23:55 +0100 |
commit | 24e977e9d9ccee34b89dfbdb7ace386afc27ed2a (patch) | |
tree | 610c27cb676521c11f3bba60a39a9bd165546fe6 | |
parent | db0834718f1e487b37cbb795cdc135e19c1defd5 (diff) |
Ha, API rewrite
-rw-r--r-- | src/vobject/lib.rs | 102 | ||||
-rw-r--r-- | tests/lib.rs | 48 |
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())); } |