From e7fc4a50d3ee4497d9113b5c73d04b88c7c129c7 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 12 Sep 2022 20:08:25 -0500 Subject: [PATCH] Calendar events ported not passing --- Cargo.toml | 1 + src/client/result/CalendarParsedResult.java | 259 --------------- src/client/result/CalendarParsedResult.rs | 313 ++++++++++++++++++ .../result/CalendarParsedResultTestCase.java | 250 -------------- .../result/CalendarParsedResultTestCase.rs | 257 ++++++++++++++ src/client/result/VCardResultParser.rs | 4 +- src/client/result/VEventResultParser.java | 118 ------- src/client/result/VEventResultParser.rs | 174 ++++++++++ src/client/result/mod.rs | 9 + src/exceptions.rs | 1 + 10 files changed, 757 insertions(+), 629 deletions(-) delete mode 100644 src/client/result/CalendarParsedResult.java create mode 100644 src/client/result/CalendarParsedResult.rs delete mode 100644 src/client/result/CalendarParsedResultTestCase.java create mode 100644 src/client/result/CalendarParsedResultTestCase.rs delete mode 100644 src/client/result/VEventResultParser.java create mode 100644 src/client/result/VEventResultParser.rs diff --git a/Cargo.toml b/Cargo.toml index 4c5b223..66a5579 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ encoding = "0.2" rand = "0.8.5" urlencoding = "2.1.2" uriparse = "0.6.4" +chrono = "0.4.22" image = {version = "0.24.3", optional = true} imageproc = {version = "0.23.0", optional = true} diff --git a/src/client/result/CalendarParsedResult.java b/src/client/result/CalendarParsedResult.java deleted file mode 100644 index 93ef6bd..0000000 --- a/src/client/result/CalendarParsedResult.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.client.result; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Represents a parsed result that encodes a calendar event at a certain time, optionally - * with attendees and a location. - * - * @author Sean Owen - */ -public final class CalendarParsedRXingResult extends ParsedRXingResult { - - private static final Pattern RFC2445_DURATION = - Pattern.compile("P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?"); - private static final long[] RFC2445_DURATION_FIELD_UNITS = { - 7 * 24 * 60 * 60 * 1000L, // 1 week - 24 * 60 * 60 * 1000L, // 1 day - 60 * 60 * 1000L, // 1 hour - 60 * 1000L, // 1 minute - 1000L, // 1 second - }; - - private static final Pattern DATE_TIME = Pattern.compile("[0-9]{8}(T[0-9]{6}Z?)?"); - - private final String summary; - private final long start; - private final boolean startAllDay; - private final long end; - private final boolean endAllDay; - private final String location; - private final String organizer; - private final String[] attendees; - private final String description; - private final double latitude; - private final double longitude; - - public CalendarParsedRXingResult(String summary, - String startString, - String endString, - String durationString, - String location, - String organizer, - String[] attendees, - String description, - double latitude, - double longitude) { - super(ParsedRXingResultType.CALENDAR); - this.summary = summary; - - try { - this.start = parseDate(startString); - } catch (ParseException pe) { - throw new IllegalArgumentException(pe.toString()); - } - - if (endString == null) { - long durationMS = parseDurationMS(durationString); - end = durationMS < 0L ? -1L : start + durationMS; - } else { - try { - this.end = parseDate(endString); - } catch (ParseException pe) { - throw new IllegalArgumentException(pe.toString()); - } - } - - this.startAllDay = startString.length() == 8; - this.endAllDay = endString != null && endString.length() == 8; - - this.location = location; - this.organizer = organizer; - this.attendees = attendees; - this.description = description; - this.latitude = latitude; - this.longitude = longitude; - } - - public String getSummary() { - return summary; - } - - /** - * @return start time - * @deprecated use {@link #getStartTimestamp()} - */ - @Deprecated - public Date getStart() { - return new Date(start); - } - - /** - * @return start time - * @see #getEndTimestamp() - */ - public long getStartTimestamp() { - return start; - } - - /** - * @return true if start time was specified as a whole day - */ - public boolean isStartAllDay() { - return startAllDay; - } - - /** - * @return event end {@link Date}, or {@code null} if event has no duration - * @deprecated use {@link #getEndTimestamp()} - */ - @Deprecated - public Date getEnd() { - return end < 0L ? null : new Date(end); - } - - /** - * @return event end {@link Date}, or -1 if event has no duration - * @see #getStartTimestamp() - */ - public long getEndTimestamp() { - return end; - } - - /** - * @return true if end time was specified as a whole day - */ - public boolean isEndAllDay() { - return endAllDay; - } - - public String getLocation() { - return location; - } - - public String getOrganizer() { - return organizer; - } - - public String[] getAttendees() { - return attendees; - } - - public String getDescription() { - return description; - } - - public double getLatitude() { - return latitude; - } - - public double getLongitude() { - return longitude; - } - - @Override - public String getDisplayRXingResult() { - StringBuilder result = new StringBuilder(100); - maybeAppend(summary, result); - maybeAppend(format(startAllDay, start), result); - maybeAppend(format(endAllDay, end), result); - maybeAppend(location, result); - maybeAppend(organizer, result); - maybeAppend(attendees, result); - maybeAppend(description, result); - return result.toString(); - } - - /** - * Parses a string as a date. RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) - * or DATE-TIME (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC). - * - * @param when The string to parse - * @throws ParseException if not able to parse as a date - */ - private static long parseDate(String when) throws ParseException { - if (!DATE_TIME.matcher(when).matches()) { - throw new ParseException(when, 0); - } - if (when.length() == 8) { - // Show only year/month/day - DateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH); - // For dates without a time, for purposes of interacting with Android, the resulting timestamp - // needs to be midnight of that day in GMT. See: - // http://code.google.com/p/android/issues/detail?id=8330 - format.setTimeZone(TimeZone.getTimeZone("GMT")); - return format.parse(when).getTime(); - } - // The when string can be local time, or UTC if it ends with a Z - if (when.length() == 16 && when.charAt(15) == 'Z') { - long milliseconds = parseDateTimeString(when.substring(0, 15)); - Calendar calendar = new GregorianCalendar(); - // Account for time zone difference - milliseconds += calendar.get(Calendar.ZONE_OFFSET); - // Might need to correct for daylight savings time, but use target time since - // now might be in DST but not then, or vice versa - calendar.setTime(new Date(milliseconds)); - return milliseconds + calendar.get(Calendar.DST_OFFSET); - } - return parseDateTimeString(when); - } - - private static String format(boolean allDay, long date) { - if (date < 0L) { - return null; - } - DateFormat format = allDay - ? DateFormat.getDateInstance(DateFormat.MEDIUM) - : DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); - return format.format(date); - } - - private static long parseDurationMS(CharSequence durationString) { - if (durationString == null) { - return -1L; - } - Matcher m = RFC2445_DURATION.matcher(durationString); - if (!m.matches()) { - return -1L; - } - long durationMS = 0L; - for (int i = 0; i < RFC2445_DURATION_FIELD_UNITS.length; i++) { - String fieldValue = m.group(i + 1); - if (fieldValue != null) { - durationMS += RFC2445_DURATION_FIELD_UNITS[i] * Integer.parseInt(fieldValue); - } - } - return durationMS; - } - - private static long parseDateTimeString(String dateTimeString) throws ParseException { - DateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH); - return format.parse(dateTimeString).getTime(); - } - -} diff --git a/src/client/result/CalendarParsedResult.rs b/src/client/result/CalendarParsedResult.rs new file mode 100644 index 0000000..b11496c --- /dev/null +++ b/src/client/result/CalendarParsedResult.rs @@ -0,0 +1,313 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// package com.google.zxing.client.result; + +// import java.text.DateFormat; +// import java.text.ParseException; +// import java.text.SimpleDateFormat; +// import java.util.Calendar; +// import java.util.Date; +// import java.util.GregorianCalendar; +// import java.util.Locale; +// import java.util.TimeZone; +// import java.util.regex.Matcher; +// import java.util.regex.Pattern; + +use chrono::{DateTime, NaiveDateTime, Utc}; +use regex::Regex; + +use crate::exceptions::Exceptions; + +use super::{ + maybe_append_multiple, maybe_append_string, ParsedRXingResult, ParsedRXingResultType, + ResultParser, +}; + +const RFC2445_DURATION: &'static str = + "P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?"; +const RFC2445_DURATION_FIELD_UNITS: [i64; 5] = [ + 7 * 24 * 60 * 60 * 1000i64, // 1 week + 24 * 60 * 60 * 1000i64, // 1 day + 60 * 60 * 1000i64, // 1 hour + 60 * 1000i64, // 1 minute + 1000i64, // 1 second +]; + +const DATE_TIME: &'static str = "[0-9]{8}(T[0-9]{6}Z?)?"; + +/** + * Represents a parsed result that encodes a calendar event at a certain time, optionally + * with attendees and a location. + * + * @author Sean Owen + */ +pub struct CalendarParsedRXingResult { + summary: String, + start: i64, + startAllDay: bool, + end: i64, + endAllDay: bool, + location: String, + organizer: String, + attendees: Vec, + description: String, + latitude: f64, + longitude: f64, +} + +impl ParsedRXingResult for CalendarParsedRXingResult { + fn getType(&self) -> super::ParsedRXingResultType { + ParsedRXingResultType::CALENDAR + } + + fn getDisplayRXingResult(&self) -> String { + let mut result = String::with_capacity(100); + maybe_append_string(&self.summary, &mut result); + maybe_append_string( + &Self::format_event(self.startAllDay, self.start), + &mut result, + ); + maybe_append_string(&Self::format_event(self.endAllDay, self.end), &mut result); + maybe_append_string(&self.location, &mut result); + maybe_append_string(&self.organizer, &mut result); + maybe_append_multiple(&self.attendees, &mut result); + maybe_append_string(&self.description, &mut result); + + result + } +} + +impl CalendarParsedRXingResult { + pub fn new( + summary: String, + startString: String, + endString: String, + durationString: String, + location: String, + organizer: String, + attendees: Vec, + description: String, + latitude: f64, + longitude: f64, + ) -> Result { + let start = Self::parseDate(startString.clone())?; + let end = if endString.is_empty() { + let durationMS = Self::parseDurationMS(&durationString); + if durationMS < 0i64 { + -1i64 + } else { + start + durationMS + } + } else { + Self::parseDate(endString.clone())? + }; + let startAllDay; + let endAllDay; + + // try { + // this.start = parseDate(startString); + // } catch (ParseException pe) { + // throw new IllegalArgumentException(pe.toString()); + // } + + // if (endString == null) { + // long durationMS = parseDurationMS(durationString); + // end = durationMS < 0L ? -1L : start + durationMS; + // } else { + // try { + // this.end = parseDate(endString); + // } catch (ParseException pe) { + // throw new IllegalArgumentException(pe.toString()); + // } + // } + + startAllDay = startString.len() == 8; + endAllDay = !endString.is_empty() && endString.len() == 8; + + Ok(Self { + summary, + start, + startAllDay, + end, + endAllDay, + location, + organizer, + attendees, + description, + latitude, + longitude, + }) + } + + /** + * Parses a string as a date. RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) + * or DATE-TIME (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC). + * + * @param when The string to parse + * @throws ParseException if not able to parse as a date + */ + fn parseDate(when: String) -> Result { + let date_time_regex = Regex::new(DATE_TIME).unwrap(); + if !date_time_regex.is_match(&when) { + return Err(Exceptions::ParseException(when)); + } + if when.len() == 8 { + // Show only year/month/day + let date_format_string = "%Y%m%d"; + // DateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH); + // For dates without a time, for purposes of interacting with Android, the resulting timestamp + // needs to be midnight of that day in GMT. See: + // http://code.google.com/p/android/issues/detail?id=8330 + let dtm = DateTime::parse_from_str(&when, date_format_string).unwrap(); + let dtm = dtm.with_timezone(&Utc); + + // format.setTimeZone(TimeZone.getTimeZone("GMT")); + // return format.parse(when).getTime(); + return Ok(dtm.timestamp()); + } + // The when string can be local time, or UTC if it ends with a Z + if when.len() == 16 && when.chars().nth(15).unwrap() == 'Z' { + let milliseconds = Self::parseDateTimeString(&when[0..15]); + // Calendar calendar = new GregorianCalendar(); + // // Account for time zone difference + // milliseconds += calendar.get(Calendar.ZONE_OFFSET); + // // Might need to correct for daylight savings time, but use target time since + // // now might be in DST but not then, or vice versa + // calendar.setTime(new Date(milliseconds)); + // return milliseconds + calendar.get(Calendar.DST_OFFSET); + return milliseconds; + } + Self::parseDateTimeString(&when) + } + + fn format_event(allDay: bool, date: i64) -> String { + if date < 0 { + return "".to_owned(); + } + let format_string = if allDay { "%F" } else { "%c" }; + // DateFormat format = allDay + // ? DateFormat.getDateInstance(DateFormat.MEDIUM) + // : DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); + // return format.format(date); + NaiveDateTime::from_timestamp(date, 0) + .format(format_string) + .to_string() + } + + fn parseDurationMS(durationString: &str) -> i64 { + if durationString.is_empty() { + return -1; + } + let regex = Regex::new(RFC2445_DURATION).unwrap(); + if let Some(m) = regex.captures(durationString) { + let mut durationMS = 0i64; + for i in 0..RFC2445_DURATION_FIELD_UNITS.len() { + // for (int i = 0; i < RFC2445_DURATION_FIELD_UNITS.length; i++) { + let fieldValue = m.get(i + 1); + if fieldValue.is_some() { + let z = fieldValue.unwrap().as_str().parse::().unwrap(); + durationMS += RFC2445_DURATION_FIELD_UNITS[i] * z; + } + } + durationMS + } else { + -1 + } + // if (!m.matches()) { + // return -1L; + // } + // long durationMS = 0L; + // for (int i = 0; i < RFC2445_DURATION_FIELD_UNITS.length; i++) { + // String fieldValue = m.group(i + 1); + // if (fieldValue != null) { + // durationMS += RFC2445_DURATION_FIELD_UNITS[i] * Integer.parseInt(fieldValue); + // } + // } + // return durationMS; + } + + fn parseDateTimeString(dateTimeString: &str) -> Result { + if let Ok(dtm) = DateTime::parse_from_str(dateTimeString, "%Y%m%dT%H%M%S") { + Ok(dtm.timestamp()) + } else { + Err(Exceptions::ParseException(format!( + "Couldn't parse {}", + dateTimeString + ))) + } + // DateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH); + // return format.parse(dateTimeString).getTime(); + } + + pub fn getSummary(&self) -> &String { + &self.summary + } + + /** + * @return start time + * @see #getEndTimestamp() + */ + pub fn getStartTimestamp(&self) -> i64 { + self.start + } + + /** + * @return true if start time was specified as a whole day + */ + pub fn isStartAllDay(&self) -> bool { + self.startAllDay + } + + /** + * @return event end {@link Date}, or -1 if event has no duration + * @see #getStartTimestamp() + */ + pub fn getEndTimestamp(&self) -> i64 { + self.end + } + + /** + * @return true if end time was specified as a whole day + */ + pub fn isEndAllDay(&self) -> bool { + self.endAllDay + } + + pub fn getLocation(&self) -> &str { + &self.location + } + + pub fn getOrganizer(&self) -> &str { + &self.organizer + } + + pub fn getAttendees(&self) -> &Vec { + &self.attendees + } + + pub fn getDescription(&self) -> &str { + &self.description + } + + pub fn getLatitude(&self) -> f64 { + self.latitude + } + + pub fn getLongitude(&self) -> f64 { + self.longitude + } +} diff --git a/src/client/result/CalendarParsedResultTestCase.java b/src/client/result/CalendarParsedResultTestCase.java deleted file mode 100644 index 9c91cdd..0000000 --- a/src/client/result/CalendarParsedResultTestCase.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.client.result; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.RXingResult; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Tests {@link CalendarParsedRXingResult}. - * - * @author Sean Owen - */ -public final class CalendarParsedRXingResultTestCase extends Assert { - - private static final double EPSILON = 1.0E-10; - - private static DateFormat makeGMTFormat() { - DateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.ENGLISH); - format.setTimeZone(TimeZone.getTimeZone("GMT")); - return format; - } - - @Before - public void setUp() { - Locale.setDefault(Locale.ENGLISH); - TimeZone.setDefault(TimeZone.getTimeZone("GMT")); - } - - @Test - public void testStartEnd() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "DTEND:20080505T234555Z\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, null, "20080504T123456Z", "20080505T234555Z"); - } - - @Test - public void testNoVCalendar() { - doTest( - "BEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "DTEND:20080505T234555Z\r\n" + - "END:VEVENT", - null, null, null, "20080504T123456Z", "20080505T234555Z"); - } - - @Test - public void testStart() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, null, "20080504T123456Z", null); - } - - @Test - public void testDuration() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "DURATION:P1D\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, null, "20080504T123456Z", "20080505T123456Z"); - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "DURATION:P1DT2H3M4S\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, null, "20080504T123456Z", "20080505T143800Z"); - } - - @Test - public void testSummary() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "SUMMARY:foo\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, "foo", null, "20080504T123456Z", null); - } - - @Test - public void testLocation() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "LOCATION:Miami\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, "Miami", "20080504T123456Z", null); - } - - @Test - public void testDescription() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "DESCRIPTION:This is a test\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - "This is a test", null, null, "20080504T123456Z", null); - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "DESCRIPTION:This is a test\r\n\t with a continuation\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - "This is a test with a continuation", null, null, "20080504T123456Z", null); - } - - @Test - public void testGeo() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "GEO:-12.345;-45.678\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, null, "20080504T123456Z", null, null, null, -12.345, -45.678); - } - - @Test - public void testBadGeo() { - // Not parsed as VEVENT - RXingResult fakeRXingResult = new RXingResult("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "GEO:-12.345\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", null, null, BarcodeFormat.QR_CODE); - ParsedRXingResult result = RXingResultParser.parseRXingResult(fakeRXingResult); - assertSame(ParsedRXingResultType.TEXT, result.getType()); - } - - @Test - public void testOrganizer() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "ORGANIZER:mailto:bob@example.org\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, null, "20080504T123456Z", null, "bob@example.org", null, Double.NaN, Double.NaN); - } - - @Test - public void testAttendees() { - doTest( - "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\n" + - "DTSTART:20080504T123456Z\r\n" + - "ATTENDEE:mailto:bob@example.org\r\n" + - "ATTENDEE:mailto:alice@example.org\r\n" + - "END:VEVENT\r\nEND:VCALENDAR", - null, null, null, "20080504T123456Z", null, null, - new String[] {"bob@example.org", "alice@example.org"}, Double.NaN, Double.NaN); - } - - @Test - public void testVEventEscapes() { - doTest("BEGIN:VEVENT\n" + - "CREATED:20111109T110351Z\n" + - "LAST-MODIFIED:20111109T170034Z\n" + - "DTSTAMP:20111109T170034Z\n" + - "UID:0f6d14ef-6cb7-4484-9080-61447ccdf9c2\n" + - "SUMMARY:Summary line\n" + - "CATEGORIES:Private\n" + - "DTSTART;TZID=Europe/Vienna:20111110T110000\n" + - "DTEND;TZID=Europe/Vienna:20111110T120000\n" + - "LOCATION:Location\\, with\\, escaped\\, commas\n" + - "DESCRIPTION:Meeting with a friend\\nlook at homepage first\\n\\n\n" + - " \\n\n" + - "SEQUENCE:1\n" + - "X-MOZ-GENERATION:1\n" + - "END:VEVENT", - "Meeting with a friend\nlook at homepage first\n\n\n \n", - "Summary line", - "Location, with, escaped, commas", - "20111110T110000Z", - "20111110T120000Z"); - } - - @Test - public void testAllDayValueDate() { - doTest("BEGIN:VEVENT\n" + - "DTSTART;VALUE=DATE:20111110\n" + - "DTEND;VALUE=DATE:20111110\n" + - "END:VEVENT", - null, null, null, "20111110T000000Z", "20111110T000000Z"); - } - - private static void doTest(String contents, - String description, - String summary, - String location, - String startString, - String endString) { - doTest(contents, description, summary, location, startString, endString, null, null, Double.NaN, Double.NaN); - } - - private static void doTest(String contents, - String description, - String summary, - String location, - String startString, - String endString, - String organizer, - String[] attendees, - double latitude, - double longitude) { - RXingResult fakeRXingResult = new RXingResult(contents, null, null, BarcodeFormat.QR_CODE); - ParsedRXingResult result = RXingResultParser.parseRXingResult(fakeRXingResult); - assertSame(ParsedRXingResultType.CALENDAR, result.getType()); - CalendarParsedRXingResult calRXingResult = (CalendarParsedRXingResult) result; - assertEquals(description, calRXingResult.getDescription()); - assertEquals(summary, calRXingResult.getSummary()); - assertEquals(location, calRXingResult.getLocation()); - DateFormat dateFormat = makeGMTFormat(); - assertEquals(startString, dateFormat.format(calRXingResult.getStartTimestamp())); - assertEquals(endString, calRXingResult.getEndTimestamp() < 0L ? null : dateFormat.format(calRXingResult.getEndTimestamp())); - assertEquals(organizer, calRXingResult.getOrganizer()); - assertArrayEquals(attendees, calRXingResult.getAttendees()); - assertEqualOrNaN(latitude, calRXingResult.getLatitude()); - assertEqualOrNaN(longitude, calRXingResult.getLongitude()); - } - - private static void assertEqualOrNaN(double expected, double actual) { - if (Double.isNaN(expected)) { - assertTrue(Double.isNaN(actual)); - } else { - assertEquals(expected, actual, EPSILON); - } - } - -} \ No newline at end of file diff --git a/src/client/result/CalendarParsedResultTestCase.rs b/src/client/result/CalendarParsedResultTestCase.rs new file mode 100644 index 0000000..720f091 --- /dev/null +++ b/src/client/result/CalendarParsedResultTestCase.rs @@ -0,0 +1,257 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// package com.google.zxing.client.result; + +// import com.google.zxing.BarcodeFormat; +// import com.google.zxing.RXingResult; +// import org.junit.Assert; +// import org.junit.Before; +// import org.junit.Test; + +// import java.text.DateFormat; +// import java.text.SimpleDateFormat; +// import java.util.Locale; +// import java.util.TimeZone; + +// /** +// * Tests {@link CalendarParsedRXingResult}. +// * +// * @author Sean Owen +// */ +// public final class CalendarParsedRXingResultTestCase extends Assert { + +use chrono::{DateTime, NaiveDateTime, Utc}; + +use crate::{ + client::result::{ParsedClientResult, ParsedRXingResultType}, + BarcodeFormat, RXingResult, +}; + +use super::{ParsedRXingResult, ResultParser}; + +const EPSILON: f64 = 1.0E-10; +const DATE_FORMAT_STRING: &'static str = "yyyyMMdd'T'HHmmss'Z'"; + +// private static DateFormat makeGMTFormat() { +// DateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.ENGLISH); +// format.setTimeZone(TimeZone.getTimeZone("GMT")); +// return format; +// } + +// @Before +// public void setUp() { +// Locale.setDefault(Locale.ENGLISH); +// TimeZone.setDefault(TimeZone.getTimeZone("GMT")); +// } + +#[test] +fn testStartEnd() { + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nDTEND:20080505T234555Z\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "", "20080504T123456Z", "20080505T234555Z"); +} + +#[test] +fn testNoVCalendar() { + doTestShort( + "BEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nDTEND:20080505T234555Z\r\nEND:VEVENT", + "", + "", + "", + "20080504T123456Z", + "20080505T234555Z", + ); +} + +#[test] +fn testStart() { + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "", "20080504T123456Z", ""); +} + +#[test] +fn testDuration() { + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nDURATION:P1D\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "", "20080504T123456Z", "20080505T123456Z"); + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nDURATION:P1DT2H3M4S\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "", "20080504T123456Z", "20080505T143800Z"); +} + +#[test] +fn testSummary() { + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nSUMMARY:foo\r\nDTSTART:20080504T123456Z\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "foo", "", "20080504T123456Z", ""); +} + +#[test] +fn testLocation() { + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nLOCATION:Miami\r\nDTSTART:20080504T123456Z\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "Miami", "20080504T123456Z", ""); +} + +#[test] +fn testDescription() { + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nDESCRIPTION:This is a test\r\nEND:VEVENT\r\nEND:VCALENDAR", + "This is a test", "", "", "20080504T123456Z", ""); + doTestShort( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nDESCRIPTION:This is a test\r\n\t with a continuation\r\nEND:VEVENT\r\nEND:VCALENDAR", + "This is a test with a continuation", "", "", "20080504T123456Z", ""); +} + +#[test] +fn testGeo() { + doTest( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nGEO:-12.345;-45.678\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "", "20080504T123456Z", "", "", &Vec::new(), -12.345, -45.678); +} + +#[test] +fn testBadGeo() { + // Not parsed as VEVENT + let fakeRXingResult = RXingResult::new( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nGEO:-12.345\r\nEND:VEVENT\r\nEND:VCALENDAR", + Vec::new(), + Vec::new(), + BarcodeFormat::QR_CODE, + ); + let result = ResultParser::parseRXingResult(&fakeRXingResult); + assert_eq!(ParsedRXingResultType::TEXT, result.getType()); +} + +#[test] +fn testOrganizer() { + doTest( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nORGANIZER:mailto:bob@example.org\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "", "20080504T123456Z", "", "bob@example.org", &Vec::new(), f64::NAN, f64::NAN); +} + +#[test] +fn testAttendees() { + doTest( + "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20080504T123456Z\r\nATTENDEE:mailto:bob@example.org\r\nATTENDEE:mailto:alice@example.org\r\nEND:VEVENT\r\nEND:VCALENDAR", + "", "", "", "20080504T123456Z", "", "", + &vec!["bob@example.org", "alice@example.org"], f64::NAN, f64::NAN); +} + +#[test] +fn testVEventEscapes() { + doTestShort("BEGIN:VEVENT\nCREATED:20111109T110351Z\nLAST-MODIFIED:20111109T170034Z\nDTSTAMP:20111109T170034Z\nUID:0f6d14ef-6cb7-4484-9080-61447ccdf9c2\nSUMMARY:Summary line\nCATEGORIES:Private\nDTSTART;TZID=Europe/Vienna:20111110T110000\nDTEND;TZID=Europe/Vienna:20111110T120000\nLOCATION:Location\\, with\\, escaped\\, commas\nDESCRIPTION:Meeting with a friend\\nlook at homepage first\\n\\n\n \\n\nSEQUENCE:1\nX-MOZ-GENERATION:1\nEND:VEVENT", + "Meeting with a friend\nlook at homepage first\n\n\n \n", + "Summary line", + "Location, with, escaped, commas", + "20111110T110000Z", + "20111110T120000Z"); +} + +#[test] +fn testAllDayValueDate() { + doTestShort( + "BEGIN:VEVENT\nDTSTART;VALUE=DATE:20111110\nDTEND;VALUE=DATE:20111110\nEND:VEVENT", + "", + "", + "", + "20111110T000000Z", + "20111110T000000Z", + ); +} + +fn doTestShort( + contents: &str, + description: &str, + summary: &str, + location: &str, + startString: &str, + endString: &str, +) { + doTest( + contents, + description, + summary, + location, + startString, + endString, + "", + &Vec::new(), + f64::NAN, + f64::NAN, + ); +} + +fn doTest( + contents: &str, + description: &str, + summary: &str, + location: &str, + startString: &str, + endString: &str, + organizer: &str, + attendees: &[&str], + latitude: f64, + longitude: f64, +) { + let fakeRXingResult = + RXingResult::new(contents, Vec::new(), Vec::new(), BarcodeFormat::QR_CODE); + let result = ResultParser::parseRXingResult(&fakeRXingResult); + assert_eq!(ParsedRXingResultType::CALENDAR, result.getType()); + if let ParsedClientResult::CalendarEventResult(calRXingResult) = result { + assert_eq!(description, calRXingResult.getDescription()); + assert_eq!(summary, calRXingResult.getSummary()); + assert_eq!(location, calRXingResult.getLocation()); + let dateFormat = "%c"; //makeGMTFormat(); + assert_eq!( + startString, + format_date_string(calRXingResult.getStartTimestamp(), dateFormat) // dateFormat.format(calRXingResult.getStartTimestamp()) + ); + assert_eq!( + endString, + if calRXingResult.getEndTimestamp() < 0i64 { + "".to_owned() + } else { + format_date_string(calRXingResult.getEndTimestamp(), dateFormat) + // dateFormat.format(calRXingResult.getEndTimestamp()) + } + ); + assert_eq!(organizer, calRXingResult.getOrganizer()); + assert_eq!(attendees, calRXingResult.getAttendees()); + assertEqualOrNaN(latitude, calRXingResult.getLatitude()); + assertEqualOrNaN(longitude, calRXingResult.getLongitude()); + } else { + panic!("Expected Calendar"); + } +} + +fn assertEqualOrNaN(expected: f64, actual: f64) { + if expected.is_nan() { + assert!(actual.is_nan()); + } else { + assert!(expected - actual < EPSILON && actual - expected < EPSILON); + // assert_eq!(expected, actual, EPSILON); + } +} + +fn format_date_string(timestamp: i64, format_string: &str) -> String { + NaiveDateTime::from_timestamp(timestamp, 0) + .format(format_string) + .to_string() + // DateTime::from(timestamp,0).with_timezone(Utc).format(format_string).to_string() +} diff --git a/src/client/result/VCardResultParser.rs b/src/client/result/VCardResultParser.rs index 2dfd086..de7a616 100644 --- a/src/client/result/VCardResultParser.rs +++ b/src/client/result/VCardResultParser.rs @@ -153,7 +153,7 @@ pub fn parse(result: &RXingResult) -> Option { } } -fn matchVCardPrefixedField( +pub fn matchVCardPrefixedField( prefix: &str, rawText: &str, trim: bool, @@ -427,7 +427,7 @@ fn maybeAppendFragment(fragmentBuffer: &mut Vec, charset: &str, result: &mut } } -fn matchSingleVCardPrefixedField( +pub fn matchSingleVCardPrefixedField( prefix: &str, rawText: &str, trim: bool, diff --git a/src/client/result/VEventResultParser.java b/src/client/result/VEventResultParser.java deleted file mode 100644 index 8bd2682..0000000 --- a/src/client/result/VEventResultParser.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.client.result; - -import com.google.zxing.RXingResult; - -import java.util.List; - -/** - * Partially implements the iCalendar format's "VEVENT" format for specifying a - * calendar event. See RFC 2445. This supports SUMMARY, LOCATION, GEO, DTSTART and DTEND fields. - * - * @author Sean Owen - */ -public final class VEventRXingResultParser extends RXingResultParser { - - @Override - public CalendarParsedRXingResult parse(RXingResult result) { - String rawText = getMassagedText(result); - int vEventStart = rawText.indexOf("BEGIN:VEVENT"); - if (vEventStart < 0) { - return null; - } - - String summary = matchSingleVCardPrefixedField("SUMMARY", rawText); - String start = matchSingleVCardPrefixedField("DTSTART", rawText); - if (start == null) { - return null; - } - String end = matchSingleVCardPrefixedField("DTEND", rawText); - String duration = matchSingleVCardPrefixedField("DURATION", rawText); - String location = matchSingleVCardPrefixedField("LOCATION", rawText); - String organizer = stripMailto(matchSingleVCardPrefixedField("ORGANIZER", rawText)); - - String[] attendees = matchVCardPrefixedField("ATTENDEE", rawText); - if (attendees != null) { - for (int i = 0; i < attendees.length; i++) { - attendees[i] = stripMailto(attendees[i]); - } - } - String description = matchSingleVCardPrefixedField("DESCRIPTION", rawText); - - String geoString = matchSingleVCardPrefixedField("GEO", rawText); - double latitude; - double longitude; - if (geoString == null) { - latitude = Double.NaN; - longitude = Double.NaN; - } else { - int semicolon = geoString.indexOf(';'); - if (semicolon < 0) { - return null; - } - try { - latitude = Double.parseDouble(geoString.substring(0, semicolon)); - longitude = Double.parseDouble(geoString.substring(semicolon + 1)); - } catch (NumberFormatException ignored) { - return null; - } - } - - try { - return new CalendarParsedRXingResult(summary, - start, - end, - duration, - location, - organizer, - attendees, - description, - latitude, - longitude); - } catch (IllegalArgumentException ignored) { - return null; - } - } - - private static String matchSingleVCardPrefixedField(CharSequence prefix, - String rawText) { - List values = VCardRXingResultParser.matchSingleVCardPrefixedField(prefix, rawText, true, false); - return values == null || values.isEmpty() ? null : values.get(0); - } - - private static String[] matchVCardPrefixedField(CharSequence prefix, String rawText) { - List> values = VCardRXingResultParser.matchVCardPrefixedField(prefix, rawText, true, false); - if (values == null || values.isEmpty()) { - return null; - } - int size = values.size(); - String[] result = new String[size]; - for (int i = 0; i < size; i++) { - result[i] = values.get(i).get(0); - } - return result; - } - - private static String stripMailto(String s) { - if (s != null && (s.startsWith("mailto:") || s.startsWith("MAILTO:"))) { - s = s.substring(7); - } - return s; - } - -} diff --git a/src/client/result/VEventResultParser.rs b/src/client/result/VEventResultParser.rs new file mode 100644 index 0000000..f4a3e61 --- /dev/null +++ b/src/client/result/VEventResultParser.rs @@ -0,0 +1,174 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// package com.google.zxing.client.result; + +// import com.google.zxing.RXingResult; + +// import java.util.List; + +use crate::RXingResult; + +use super::{CalendarParsedRXingResult, ParsedClientResult, ResultParser, VCardResultParser}; + +/** + * Partially implements the iCalendar format's "VEVENT" format for specifying a + * calendar event. See RFC 2445. This supports SUMMARY, LOCATION, GEO, DTSTART and DTEND fields. + * + * @author Sean Owen + */ +pub fn parse(result: &RXingResult) -> Option { + let rawText = ResultParser::getMassagedText(result); + if let None = rawText.find("BEGIN:VEVENT") { + return None; + } + // if (vEventStart < 0) { + // return null; + // } + + let summary = matchSingleVCardPrefixedField("SUMMARY", &rawText); + let start = matchSingleVCardPrefixedField("DTSTART", &rawText); + if start.is_empty() { + return None; + } + let end = matchSingleVCardPrefixedField("DTEND", &rawText); + let duration = matchSingleVCardPrefixedField("DURATION", &rawText); + let location = matchSingleVCardPrefixedField("LOCATION", &rawText); + let organizer = stripMailto(&matchSingleVCardPrefixedField("ORGANIZER", &rawText)); + + let mut attendees = matchVCardPrefixedField("ATTENDEE", &rawText); + if !attendees.is_empty() { + for i in 0..attendees.len() { + // for (int i = 0; i < attendees.length; i++) { + attendees[i] = stripMailto(&attendees[i]); + } + } + let description = matchSingleVCardPrefixedField("DESCRIPTION", &rawText); + + let geoString = matchSingleVCardPrefixedField("GEO", &rawText); + let latitude; + let longitude; + if geoString.is_empty() { + latitude = f64::NAN; + longitude = f64::NAN; + } else { + if let Some(semicolon) = geoString.find(';') { + latitude = if let Ok(l) = geoString[..semicolon].parse() { + l + } else { + return None; + }; + longitude = if let Ok(l) = geoString[semicolon + 1..].parse() { + l + } else { + return None; + } + } else { + return None; + } + // if (semicolon < 0) { + // return null; + // } + // try { + // latitude = Double.parseDouble(geoString.substring(0, semicolon)); + // longitude = Double.parseDouble(geoString.substring(semicolon + 1)); + // } catch (NumberFormatException ignored) { + // return null; + // } + } + + if let Ok(cpr) = CalendarParsedRXingResult::new( + summary, + start, + end, + duration, + location, + organizer, + attendees, + description, + latitude, + longitude, + ) { + Some(ParsedClientResult::CalendarEventResult(cpr)) + } else { + None + } + + // try { + // return new CalendarParsedRXingResult(summary, + // start, + // end, + // duration, + // location, + // organizer, + // attendees, + // description, + // latitude, + // longitude); + // } catch (IllegalArgumentException ignored) { + // return null; + // } +} + +fn matchSingleVCardPrefixedField(prefix: &str, rawText: &str) -> String { + if let Some(values) = + VCardResultParser::matchSingleVCardPrefixedField(prefix, rawText, true, false) + { + if values.is_empty() { + "".to_owned() + } else { + values.get(0).unwrap().clone() + } + } else { + "".to_owned() + } + // return values == null || values.isEmpty() ? null : values.get(0); +} + +fn matchVCardPrefixedField(prefix: &str, rawText: &str) -> Vec { + if let Some(values) = VCardResultParser::matchVCardPrefixedField(prefix, rawText, true, false) { + if values.is_empty() { + Vec::new() + } else { + let size = values.len(); + let mut result = vec!["".to_owned(); size]; //new String[size]; + for i in 0..size { + // for (int i = 0; i < size; i++) { + result[i] = values.get(i).unwrap().get(0).unwrap().clone(); + } + result + } + } else { + Vec::new() + } + // if values == null || values.isEmpty() { + // return null; + // } + // int size = values.size(); + // String[] result = new String[size]; + // for (int i = 0; i < size; i++) { + // result[i] = values.get(i).get(0); + // } + // return result; +} + +fn stripMailto(s: &str) -> String { + let mut s = s; + if !s.is_empty() && (s.starts_with("mailto:") || s.starts_with("MAILTO:")) { + s = &s[7..]; + } + s.to_owned() +} diff --git a/src/client/result/mod.rs b/src/client/result/mod.rs index 4391c0c..527ab8d 100644 --- a/src/client/result/mod.rs +++ b/src/client/result/mod.rs @@ -31,6 +31,8 @@ mod AddressBookDoCoMoResultParser; mod AddressBookAUResultParser; mod VCardResultParser; mod BizcardResultParser; +mod CalendarParsedResult; +mod VEventResultParser; use std::fmt; @@ -52,6 +54,8 @@ pub use URIParsedResult::*; pub use EmailAddressParsedResult::*; pub use VINParsedResult::*; pub use AddressBookParsedResult::*; +pub use CalendarParsedResult::*; +pub use CalendarParsedResult::*; #[cfg(test)] mod TelParsedResultTestCase; @@ -73,6 +77,8 @@ mod EmailAddressParsedResultTestCase; mod VINParsedResultTestCase; #[cfg(test)] mod AddressBookParsedResultTestCase; +#[cfg(test)] +mod CalendarParsedResultTestCase; pub enum ParsedClientResult { TextResult(TextParsedRXingResult), @@ -86,6 +92,7 @@ pub enum ParsedClientResult { EmailResult(EmailAddressParsedRXingResult), VINResult(VINParsedRXingResult), AddressBookResult(AddressBookParsedRXingResult), + CalendarEventResult(CalendarParsedRXingResult), } impl ParsedRXingResult for ParsedClientResult { @@ -102,6 +109,7 @@ impl ParsedRXingResult for ParsedClientResult { ParsedClientResult::EmailResult(a) => a.getType(), ParsedClientResult::VINResult(a) => a.getType(), ParsedClientResult::AddressBookResult(a) => a.getType(), + ParsedClientResult::CalendarEventResult(a) => a.getType(), } } @@ -118,6 +126,7 @@ impl ParsedRXingResult for ParsedClientResult { ParsedClientResult::EmailResult(a) => a.getDisplayRXingResult(), ParsedClientResult::VINResult(a) => a.getDisplayRXingResult(), ParsedClientResult::AddressBookResult(a) => a.getDisplayRXingResult(), + ParsedClientResult::CalendarEventResult(a) => a.getDisplayRXingResult(), } } } diff --git a/src/exceptions.rs b/src/exceptions.rs index 827b77a..592282e 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -14,5 +14,6 @@ pub enum Exceptions { ReedSolomonException(String), IndexOutOfBoundsException(String), RuntimeException(String), + ParseException(String), ReaderDecodeException() } \ No newline at end of file