Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Builder for creating vCards.
//!
use crate::{
property::{DeliveryAddress, Gender, Kind, TextListProperty},
Date, DateTime, Uri, Vcard,
property::{DeliveryAddress, Gender, Kind, TextListProperty},
};

#[cfg(feature = "language-tags")]
Expand Down
10 changes: 3 additions & 7 deletions src/date_time.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::Error;
use std::{fmt, str::FromStr};
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
use time::{OffsetDateTime, format_description::well_known::Rfc3339};

#[cfg(feature = "serde")]
use serde_with::{serde_as, DeserializeFromStr, SerializeDisplay};
use serde_with::{DeserializeFromStr, SerializeDisplay, serde_as};

/// Date and time that serializes to and from RFC3339.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -38,11 +38,7 @@ impl AsRef<OffsetDateTime> for DateTime {

impl fmt::Display for DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
self.0.format(&Rfc3339).map_err(|_| fmt::Error)?
)
write!(f, "{}", self.0.format(&Rfc3339).map_err(|_| fmt::Error)?)
}
}

Expand Down
19 changes: 10 additions & 9 deletions src/helper.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Utilities for parsing dates, times and primitive values.
use std::fmt;
use time::{
format_description::{self, well_known::Iso8601},
Date, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset,
format_description::{self, well_known::Iso8601},
};

use crate::{property::DateAndOrTime, DateTime, Error, Result};
use crate::{DateTime, Error, Result, property::DateAndOrTime};

// UTC OFFSET

Expand Down Expand Up @@ -155,9 +155,10 @@ pub fn parse_date(value: &str) -> Result<Date> {
*val = "00";
}
if let Some(val) = parts.get_mut(3)
&& *val == "-" {
*val = "01";
}
&& *val == "-"
{
*val = "01";
}

let value = parts.join("");
do_parse_date(&value)
Expand Down Expand Up @@ -279,11 +280,11 @@ pub(crate) fn format_date_time_list(
/// Parse a timestamp.
pub fn parse_timestamp(value: &str) -> Result<DateTime> {
let offset_format = format_description::parse(
"[year][month][day]T[hour][minute][second][offset_hour sign:mandatory][offset_minute]",
)?;
"[year][month][day]T[hour][minute][second][offset_hour sign:mandatory][offset_minute]",
)?;
let offset_format_hours = format_description::parse(
"[year][month][day]T[hour][minute][second][offset_hour sign:mandatory]",
)?;
"[year][month][day]T[hour][minute][second][offset_hour sign:mandatory]",
)?;
let utc_format = format_description::parse(
"[year][month][day]T[hour][minute][second]Z",
)?;
Expand Down
2 changes: 1 addition & 1 deletion src/iter.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Iterator for parsing vCards.
use crate::{
parser::{Token, VcardParser},
Error, Result, Vcard,
parser::{Token, VcardParser},
};
use std::ops::Range;

Expand Down
33 changes: 20 additions & 13 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use language_tags::LanguageTag;
use mime::Mime;

use crate::{
error::LexError, escape_control, helper::*, name::*, parameter::*,
property::*, unescape_value, Error, Result, Uri, Vcard,
Error, Result, Uri, Vcard, error::LexError, escape_control, helper::*,
name::*, parameter::*, property::*, unescape_value,
};

type LexResult<T> = std::result::Result<T, LexError>;
Expand All @@ -33,7 +33,9 @@ pub(crate) enum Token {
#[token("GEO")]
Geo,

#[regex("(?i:([a-z0-9-]+\\.)?(SOURCE|KIND|FN|N|NICKNAME|PHOTO|BDAY|ANNIVERSARY|GENDER|ADR|TEL|EMAIL|IMPP|LANG|TITLE|ROLE|LOGO|ORG|MEMBER|RELATED|CATEGORIES|NOTE|PRODID|REV|SOUND|UID|CLIENTPIDMAP|URL|KEY|FBURL|CALADRURI|CALURI|XML|VERSION|(X-[a-z0-9-]+)))")]
#[regex(
"(?i:([a-z0-9-]+\\.)?(SOURCE|KIND|FN|N|NICKNAME|PHOTO|BDAY|ANNIVERSARY|GENDER|ADR|TEL|EMAIL|IMPP|LANG|TITLE|ROLE|LOGO|ORG|MEMBER|RELATED|CATEGORIES|NOTE|PRODID|REV|SOUND|UID|CLIENTPIDMAP|URL|KEY|FBURL|CALADRURI|CALURI|XML|VERSION|(X-[a-z0-9-]+)))"
)]
PropertyName,

#[regex("(?i:x-[a-z0-9-]+)")]
Expand All @@ -45,7 +47,9 @@ pub(crate) enum Token {
#[token("\"")]
DoubleQuote,

#[regex("(?i:LANGUAGE|VALUE|PREF|ALTID|PID|TYPE|MEDIATYPE|CALSCALE|SORT-AS|CHARSET|LABEL|ENCODING)")]
#[regex(
"(?i:LANGUAGE|VALUE|PREF|ALTID|PID|TYPE|MEDIATYPE|CALSCALE|SORT-AS|CHARSET|LABEL|ENCODING)"
)]
ParameterKey,

#[token("=")]
Expand Down Expand Up @@ -170,9 +174,10 @@ impl<'s> VcardParser<'s> {
)?;

if let Err(e) = self.parse_property(lex, first, card)
&& self.strict {
return Err(e);
}
&& self.strict
{
return Err(e);
}
}
Ok(())
}
Expand Down Expand Up @@ -282,7 +287,8 @@ impl<'s> VcardParser<'s> {
if self.strict {
let value: ValueType = value.parse()?;
params.value = Some(value);
} else if let Ok(value) = value.parse::<ValueType>()
} else if let Ok(value) =
value.parse::<ValueType>()
{
params.value = Some(value);
}
Expand Down Expand Up @@ -399,7 +405,7 @@ impl<'s> VcardParser<'s> {
_ => {
return Err(Error::UnknownParameter(
parameter_name.to_string(),
))
));
}
}
}
Expand Down Expand Up @@ -710,7 +716,7 @@ impl<'s> VcardParser<'s> {
return Err(Error::UnsupportedValueType(
value_type.to_string(),
upper_name,
))
));
}
}
} else {
Expand Down Expand Up @@ -850,9 +856,10 @@ impl<'s> VcardParser<'s> {
}
CLIENTPIDMAP => {
if let Some(params) = &parameters
&& params.pid.is_some() {
return Err(Error::ClientPidMapPidNotAllowed);
}
&& params.pid.is_some()
{
return Err(Error::ClientPidMapPidNotAllowed);
}

let value: ClientPidMap = value.as_ref().parse()?;
card.client_pid_map.push(ClientPidMapProperty {
Expand Down
5 changes: 2 additions & 3 deletions src/property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ use language_tags::LanguageTag;
use serde::{Deserialize, Serialize};

#[cfg(feature = "serde")]
use serde_with::{serde_as, DisplayFromStr};
use serde_with::{DisplayFromStr, serde_as};

#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};

use crate::{
escape_value,
Date, DateTime, Error, Result, Uri, escape_value,
helper::{
format_date, format_date_and_or_time_list, format_date_list,
format_date_time, format_date_time_list, format_float_list,
Expand All @@ -28,7 +28,6 @@ use crate::{
parse_date_time, parse_time, parse_utc_offset,
},
parameter::Parameters,
Date, DateTime, Error, Result, Uri,
};

const INDIVIDUAL: &str = "individual";
Expand Down
38 changes: 20 additions & 18 deletions src/vcard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};

use base64::{engine::general_purpose, Engine};
use base64::{Engine, engine::general_purpose};

use crate::{iter, property::*, Error, Result};
use crate::{Error, Result, iter, property::*};

/// The vCard type.
#[derive(Debug, Default, Eq, PartialEq, Clone)]
Expand Down Expand Up @@ -299,21 +299,20 @@ impl Vcard {
for photo in self.photo.iter() {
if let TextOrUriProperty::Text(prop) = photo
&& let Some(params) = &prop.parameters
&& let (Some(types), Some(extensions)) =
(&params.types, &params.extensions)
&& let (
Some(TypeParameter::Extension(value)),
Some((name, values)),
) = (types.first(), extensions.first())
&& name.to_uppercase() == "ENCODING"
&& values.first() == Some(&"b".to_string())
&& &value.to_uppercase() == "JPEG"
{
let encoded = &prop.value;
let buffer = general_purpose::STANDARD
.decode(encoded)?;
jpegs.push(buffer);
}
&& let (Some(types), Some(extensions)) =
(&params.types, &params.extensions)
&& let (
Some(TypeParameter::Extension(value)),
Some((name, values)),
) = (types.first(), extensions.first())
&& name.to_uppercase() == "ENCODING"
&& values.first() == Some(&"b".to_string())
&& &value.to_uppercase() == "JPEG"
{
let encoded = &prop.value;
let buffer = general_purpose::STANDARD.decode(encoded)?;
jpegs.push(buffer);
}
}
Ok(jpegs)
}
Expand Down Expand Up @@ -492,8 +491,11 @@ fn fold_line(line: String, wrap_at: usize) -> String {
let mut folded_line = String::new();
for grapheme in UnicodeSegmentation::graphemes(&line[..], true) {
length += grapheme.len();
if length % wrap_at == 0 {
if length > wrap_at {
folded_line.push_str("\r\n ");
// actual length of the next line
// including the leading space
length = 1 + grapheme.len();
}
folded_line.push_str(grapheme);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/errors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod test_helpers;

use anyhow::Result;
use vcard4::{helper::*, parameter::*, parse, property::ClientPidMap, Error};
use vcard4::{Error, helper::*, parameter::*, parse, property::ClientPidMap};

#[test]
fn error_empty() -> Result<()> {
Expand Down
14 changes: 7 additions & 7 deletions tests/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,19 @@ END:VCARD"#;
assert_round_trip(&card)?;

/*
let input = r#"BEGIN:VCARD
let input = r#"BEGIN:VCARD
VERSION:4.0
KIND:org
FN:ABC Marketing
ORG:ABC\, Inc.;North American Division;Marketing
END:VCARD"#;
let mut vcards = parse(input)?;
assert_eq!(1, vcards.len());
let card = vcards.remove(0);
let mut vcards = parse(input)?;
assert_eq!(1, vcards.len());
let card = vcards.remove(0);

assert_eq!(Kind::Org, card.kind.as_ref().unwrap().value);
assert_round_trip(&card)?;
*/
assert_eq!(Kind::Org, card.kind.as_ref().unwrap().value);
assert_round_trip(&card)?;
*/
Ok(())
}

Expand Down
10 changes: 6 additions & 4 deletions tests/identification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ END:VCARD"#;
}

if let TextOrUriProperty::Uri(photo2) = card.photo.get(1).unwrap() {
assert!(photo2
.value
.to_string()
.starts_with("data:image/jpeg;base64,"));
assert!(
photo2
.value
.to_string()
.starts_with("data:image/jpeg;base64,")
);
assert!(photo2.value.to_string().ends_with("TeXN0"));
} else {
panic!("expecting URI property");
Expand Down
12 changes: 7 additions & 5 deletions tests/organizational.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod test_helpers;

use anyhow::Result;
use test_helpers::assert_round_trip;
use vcard4::{parameter::TypeParameter, parse, property::*, Uri};
use vcard4::{Uri, parameter::TypeParameter, parse, property::*};

#[test]
fn organizational_title() -> Result<()> {
Expand Down Expand Up @@ -61,10 +61,12 @@ END:VCARD"#;
&logo1.value.to_string()
);

assert!(logo2
.value
.to_string()
.starts_with("data:image/jpeg;base64,"));
assert!(
logo2
.value
.to_string()
.starts_with("data:image/jpeg;base64,")
);
assert!(logo2.value.to_string().ends_with("TeXN0"));

assert_round_trip(&card)?;
Expand Down
2 changes: 1 addition & 1 deletion tests/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ mod test_helpers;
use anyhow::Result;

use vcard4::{
Error,
helper::parse_utc_offset,
parameter::{
Pid, RelatedType, TelephoneType, TimeZoneParameter, TypeParameter,
ValueType,
},
parse, parse_loose,
property::{AnyProperty, TextOrUriProperty},
Error,
};

use test_helpers::{assert_language, assert_media_type, assert_round_trip};
Expand Down
2 changes: 1 addition & 1 deletion tests/test_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::Result;

use vcard4::{parameter::Parameters, parse, Vcard};
use vcard4::{Vcard, parameter::Parameters, parse};

#[cfg(feature = "language-tags")]
use language_tags::LanguageTag;
Expand Down
15 changes: 15 additions & 0 deletions tests/unicode_fold_line.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use anyhow::Result;
use vcard4::Vcard;

const EXPECTED: &str = "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:12345678901234567890123456789012345678901234567890123456789012345678901\r\n ö123456789012345678901234567890123456789012345678901234567890123456789012\r\n 34567890123456789012345678901234567890123456789012345678901234567890123456\r\n 78901234567890123456789012345678901234567890123456789012345678901234567890\r\n 123456789012345678901234567890123456789012345678901234567890\r\nEND:VCARD\r\n";

#[test]
fn unicode_fold_line() -> Result<()> {
let line70 = "1234567890".repeat(7);
let line76 = format!("{line70}1ö{line70}{line70}{line70}{line70}");
let mut card: Vcard = Default::default();
card.formatted_name = vec![line76.into()];
let result = card.to_string();
assert_eq!(result, EXPECTED);
Ok(())
}