1use std::time::Duration;
14use strcursor::StrCursor;
15use ::ScanError;
16use ::input::ScanInput;
17use ::scanner::ScanFromStr;
18use ::util::MsgErr;
19
20pub enum Iso8601Duration {}
51
52impl<'a> ScanFromStr<'a> for Iso8601Duration {
53 type Output = Duration;
54 fn scan_from<I: ScanInput<'a>>(s: I) -> Result<(Self::Output, usize), ScanError> {
55 let cur = StrCursor::new_at_start(s.as_str());
56 let (dur, cur) = try!(scan_8601(cur));
57 Ok((dur, cur.byte_pos()))
58 }
59}
60
61const SECS_IN_SEC: u64 = 1;
62const SECS_IN_MIN: u64 = 60;
63const SECS_IN_HOUR: u64 = 60 * SECS_IN_MIN;
64
65#[cfg(feature="duration-iso8601-dates")] const SECS_IN_DAY: u64 = 24 * SECS_IN_HOUR;
66#[cfg(feature="duration-iso8601-dates")] const SECS_IN_WEEK: u64 = 7 * SECS_IN_DAY;
67#[cfg(feature="duration-iso8601-dates")] const SECS_IN_MONTH: u64 = 30 * SECS_IN_DAY + 10 * SECS_IN_HOUR + 30 * SECS_IN_MIN;
68#[cfg(feature="duration-iso8601-dates")] const SECS_IN_YEAR: u64 = 365 * SECS_IN_DAY + 6 * SECS_IN_HOUR;
69
70const NANOS_IN_SEC: u32 = 1_000_000_000;
71
72#[cfg(not(feature="duration-iso8601-dates"))]
73#[cfg(test)]
74#[test]
75fn test_iso_8601_duration_dates() {
76 use ::ScanError as SE;
77 use ::ScanErrorKind as SEK;
78
79 let scan = Iso8601Duration::scan_from;
80
81 assert_match!(
82 scan("P1W"),
83 Err(SE { kind: SEK::Syntax(_), ref at, .. }) if at.offset() == 0
84 );
85 assert_match!(
86 scan("P1Y"),
87 Err(SE { kind: SEK::Syntax(_), ref at, .. }) if at.offset() == 0
88 );
89 assert_match!(
90 scan("P1M"),
91 Err(SE { kind: SEK::Syntax(_), ref at, .. }) if at.offset() == 0
92 );
93 assert_match!(
94 scan("P1D"),
95 Err(SE { kind: SEK::Syntax(_), ref at, .. }) if at.offset() == 0
96 );
97}
98
99#[cfg(feature="duration-iso8601-dates")]
100#[cfg(test)]
101#[test]
102fn test_iso_8601_duration_dates() {
103 let scan = Iso8601Duration::scan_from;
104
105 assert_match!(
106 scan("P1W"),
107 Ok((d, 3)) if d == Duration::new(SECS_IN_WEEK, 0)
108 );
109 assert_match!(
110 scan("P1Y"),
111 Ok((d, 3)) if d == Duration::new(SECS_IN_YEAR, 0)
112 );
113 assert_match!(
114 scan("P1M"),
115 Ok((d, 3)) if d == Duration::new(SECS_IN_MONTH, 0)
116 );
117 assert_match!(
118 scan("P1D"),
119 Ok((d, 3)) if d == Duration::new(SECS_IN_DAY, 0)
120 );
121
122 assert_match!(
123 scan("P1Y2M3DT4H5M6.7S"),
124 Ok((d, 16)) if d == Duration::new(
125 1*SECS_IN_YEAR
126 + 2*SECS_IN_MONTH
127 + 3*SECS_IN_DAY
128 + 4*SECS_IN_HOUR
129 + 5*SECS_IN_MIN
130 + 6*SECS_IN_SEC,
131 700_000_000
132 )
133 );
134
135 assert_match!(
136 scan("P6789-01-23T12:34:56"),
137 Ok((d, 20)) if d == Duration::new(
138 6789*SECS_IN_YEAR
139 + 01*SECS_IN_MONTH
140 + 23*SECS_IN_DAY
141 + 12*SECS_IN_HOUR
142 + 34*SECS_IN_MIN
143 + 56*SECS_IN_SEC,
144 0
145 )
146 );
147
148 assert_match!(
149 scan("P67890123T123456"),
150 Ok((d, 16)) if d == Duration::new(
151 6789*SECS_IN_YEAR
152 + 01*SECS_IN_MONTH
153 + 23*SECS_IN_DAY
154 + 12*SECS_IN_HOUR
155 + 34*SECS_IN_MIN
156 + 56*SECS_IN_SEC,
157 0
158 )
159 );
160}
161
162#[cfg(test)]
163#[test]
164fn test_iso_8601_duration() {
165 use ::ScanError as SE;
166 use ::ScanErrorKind as SEK;
167
168 let scan = Iso8601Duration::scan_from;
169
170 assert_match!(scan("PT1H"), Ok((d, 4)) if d == Duration::new(SECS_IN_HOUR, 0));
171 assert_match!(scan("PT1M"), Ok((d, 4)) if d == Duration::new(SECS_IN_MIN, 0));
172 assert_match!(scan("PT1S"), Ok((d, 4)) if d == Duration::new(SECS_IN_SEC, 0));
173 assert_match!(
174 scan("PT12H34M56.78S"),
175 Ok((d, 14)) if d == Duration::new(12*SECS_IN_HOUR + 34*SECS_IN_MIN + 56, 780_000_000)
176 );
177 assert_match!(
178 scan("PT34M56.78S"),
179 Ok((d, 11)) if d == Duration::new(34*SECS_IN_MIN + 56, 780_000_000)
180 );
181 assert_match!(
182 scan("PT12H56.78S"),
183 Ok((d, 11)) if d == Duration::new(12*SECS_IN_HOUR + 56, 780_000_000)
184 );
185 assert_match!(
186 scan("PT12H34M56S"),
187 Ok((d, 11)) if d == Duration::new(12*SECS_IN_HOUR + 34*SECS_IN_MIN + 56, 0)
188 );
189 assert_match!(
190 scan("PT12H34M"),
191 Ok((d, 8)) if d == Duration::new(12*SECS_IN_HOUR + 34*SECS_IN_MIN, 0)
192 );
193
194 assert_match!(
195 scan("PT0.5H"),
196 Ok((d, 6)) if d == Duration::new(30*SECS_IN_MIN, 0)
197 );
198 assert_match!(
199 scan("PT0.5M"),
200 Ok((d, 6)) if d == Duration::new(30*SECS_IN_SEC, 0)
201 );
202 assert_match!(
203 scan("PT0.5S"),
204 Ok((d, 6)) if d == Duration::new(0, NANOS_IN_SEC/2)
205 );
206
207 assert_match!(
208 scan("PT0.000000001S"),
209 Ok((d, 14)) if d == Duration::new(0, 1)
210 );
211 assert_match!(
212 scan("PT0.000000000016666666666666667M"),
213 Ok((d, 32)) if d == Duration::new(0, 1)
214 );
215 assert_match!(
216 scan("PT0.0000000000002777777777777778H"),
217 Ok((d, 33)) if d == Duration::new(0, 1)
218 );
219
220 assert_match!(
221 scan(""),
222 Err(SE { kind: SEK::Syntax(_), .. })
223 );
224 assert_match!(
225 scan("a while"),
226 Err(SE { kind: SEK::Syntax(_), .. })
227 );
228 assert_match!(
229 scan("P"),
230 Err(SE { kind: SEK::Syntax(_), .. })
231 );
232 assert_match!(
233 scan("Px"),
234 Err(SE { kind: SEK::Syntax(_), .. })
235 );
236 assert_match!(
237 scan("PY"),
238 Err(SE { kind: SEK::Syntax(_), .. })
239 );
240 assert_match!(
241 scan("PM"),
242 Err(SE { kind: SEK::Syntax(_), .. })
243 );
244 assert_match!(
245 scan("PD"),
246 Err(SE { kind: SEK::Syntax(_), .. })
247 );
248 assert_match!(
249 scan("PW"),
250 Err(SE { kind: SEK::Syntax(_), .. })
251 );
252 assert_match!(
253 scan("P1H"),
254 Err(SE { kind: SEK::Syntax(_), .. })
255 );
256 assert_match!(
257 scan("P1S"),
258 Err(SE { kind: SEK::Syntax(_), .. })
259 );
260}
261
262type ScanResult<T, L=usize> = Result<(T, L), ScanError>;
263
264fn scan_8601(cur: StrCursor) -> ScanResult<Duration, StrCursor> {
265 let cur = match cur.next_cp() {
271 Some(('P', cur)) => cur,
272 _ => return Err(ScanError::syntax("expected `P`")
273 .add_offset(cur.byte_pos()))
274 };
275
276 return match cur.next_cp() {
277 Some(('T', cur)) => given_date(Duration::new(0, 0), cur),
278 _ => date(cur)
279 };
280
281 #[cfg(not(feature="duration-iso8601-dates"))]
282 fn date(_: StrCursor) -> ScanResult<Duration, StrCursor> {
283 Err(ScanError::syntax("durations with date components not supported"))
284 }
285
286 #[cfg(feature="duration-iso8601-dates")]
287 fn date(cur: StrCursor) -> ScanResult<Duration, StrCursor> {
288 let (int, int_cur) = try!(scan_integer(cur));
289 let int_len = cur.slice_between(int_cur).unwrap().len();
290
291 match int_cur.next_cp() {
292 Some(('.', cur)) | Some((',', cur)) => date_leading_frac(int, cur),
293 Some(('T', cur)) => {
294 if int_len != "YYYYMMDD".len() {
295 return Err(ScanError::syntax("expected date in `YYYYMMDD` format")
296 .add_offset(cur.byte_pos()));
297 }
298 time_compound(int, cur)
299 },
300 Some(('-', cur)) => {
301 if int_len != "YYYY".len() {
302 return Err(ScanError::syntax("expected year in `YYYY-MM-DD` format")
303 .add_offset(cur.byte_pos()));
304 }
305 date_split_month(try!(dur_years(int, 0.0)), cur)
306 },
307 Some(('Y', cur)) => {
308 let y = try!(dur_years(int, 0.0));
309 given_year(y, cur)
310 },
311 Some(('M', cur)) => {
312 let m = try!(dur_months(int, 0.0));
313 given_month(m, cur)
314 },
315 Some(('D', cur)) => {
316 let d = try!(dur_days(int, 0.0));
317 given_day(d, cur)
318 },
319 Some(('W', cur)) => {
320 let w = try!(dur_weeks(int, 0.0));
321 Ok((w, cur))
322 },
323 _ => Err(ScanError::syntax("expected number followed by one of `T`, `Y`, `M`, `D`, or `W`")
324 .add_offset(cur.byte_pos()))
325 }
326 }
327
328 #[cfg(feature="duration-iso8601-dates")]
329 fn date_leading_frac(int: u64, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
330 let ((int, frac), cur) = try!(scan_real_frac(int, cur));
331 match cur.next_cp() {
332 Some(('Y', cur)) => given_year(try!(dur_years(int, frac)), cur),
333 Some(('M', cur)) => given_month(try!(dur_months(int, frac)), cur),
334 Some(('D', cur)) => given_day(try!(dur_days(int, frac)), cur),
335 Some(('W', cur)) => {
336 let w = try!(dur_weeks(int, frac));
337 Ok((w, cur))
338 },
339 _ => Err(ScanError::syntax("expected real number followed by one of `Y`, `M`, `D`, or `W`")
340 .add_offset(cur.byte_pos()))
341 }
342 }
343
344 #[cfg(feature="duration-iso8601-dates")]
345 fn time_compound(date: u64, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
346 let (time, time_cur) = try!(scan_integer(cur));
347 let time_len = cur.slice_between(time_cur).unwrap().len();
348
349 if time_len != "HHMMSS".len() {
350 return Err(ScanError::syntax("expected time in `hhmmss` format")
351 .add_offset(cur.byte_pos()));
352 }
353
354 let years = date / 1_00_00;
355 let months = (date / 1_00) % 1_00;
356 let days = date % 1_00;
357
358 if months > 12 {
359 return Err(ScanError::syntax("months cannot exceed 12 in this format"));
360 }
361
362 if days > 31 {
363 return Err(ScanError::syntax("days cannot exceed 31 in this format"));
364 }
365
366 let hours = time / 1_00_00;
367 let mins = (time / 1_00) % 1_00;
368 let secs = time % 1_00;
369
370 if hours > 24 {
371 return Err(ScanError::syntax("hours cannot exceed 24 in this format"));
372 }
373
374 if mins > 60 {
375 return Err(ScanError::syntax("minutes cannot exceed 60 in this format"));
376 }
377
378 if secs > 61 {
379 return Err(ScanError::syntax("days cannot exceed 61 in this format"));
380 }
381
382 let years_dur = try!(dur_years(years, 0.0));
383 let months_dur = try!(dur_months(months, 0.0));
384 let days_dur = try!(dur_days(days, 0.0));
385 let hours_dur = try!(dur_hours(hours, 0.0));
386 let mins_dur = try!(dur_mins(mins, 0.0));
387 let secs_dur = try!(dur_secs(secs, 0.0));
388
389 checked_add_dur(years_dur, months_dur)
390 .and_then(|lhs| checked_add_dur(lhs, days_dur))
391 .and_then(|lhs| checked_add_dur(lhs, hours_dur))
392 .and_then(|lhs| checked_add_dur(lhs, mins_dur))
393 .and_then(|lhs| checked_add_dur(lhs, secs_dur))
394 .map(|dur| (dur, time_cur))
395 .ok_or_else(|| ScanError::other(MsgErr("overflow in duration")))
396 }
397
398 #[cfg(feature="duration-iso8601-dates")]
399 fn date_split_month(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
400 let (months, months_cur) = try!(scan_integer(cur));
401 let months_len = cur.slice_between(months_cur).unwrap().len();
402
403 if months_len != "MM".len() {
404 return Err(ScanError::syntax("expected month in `YYYY-MM-DD` format")
405 .add_offset(cur.byte_pos()));
406 }
407
408 match months_cur.next_cp() {
409 Some(('-', cur)) => date_split_day(dur + try!(dur_months(months, 0.0)), cur),
410 _ => Err(ScanError::syntax("expected `-` after month in `YYYY-MM-DD` format")
411 .add_offset(cur.byte_pos()))
412 }
413 }
414
415 #[cfg(feature="duration-iso8601-dates")]
416 fn date_split_day(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
417 let (days, days_cur) = try!(scan_integer(cur));
418 let days_len = cur.slice_between(days_cur).unwrap().len();
419
420 if days_len != "DD".len() {
421 return Err(ScanError::syntax("expected day in `YYYY-MM-DD` format")
422 .add_offset(cur.byte_pos()));
423 }
424
425 match days_cur.next_cp() {
426 Some(('T', cur)) => date_split_hour(dur + try!(dur_days(days, 0.0)), cur),
427 _ => Err(ScanError::syntax("expected `T` following date")
428 .add_offset(cur.byte_pos()))
429 }
430 }
431
432 #[cfg(feature="duration-iso8601-dates")]
433 fn date_split_hour(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
434 let (hours, hours_cur) = try!(scan_integer(cur));
435 let hours_len = cur.slice_between(hours_cur).unwrap().len();
436
437 if hours_len != "hh".len() {
438 return Err(ScanError::syntax("expected time in `hh:mm:ss` format")
439 .add_offset(cur.byte_pos()));
440 }
441
442 match hours_cur.next_cp() {
443 Some((':', cur)) => date_split_min(dur + try!(dur_hours(hours, 0.0)), cur),
444 _ => Err(ScanError::syntax("expected time in `hh:mm:ss` format")
445 .add_offset(cur.byte_pos()))
446 }
447 }
448
449 #[cfg(feature="duration-iso8601-dates")]
450 fn date_split_min(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
451 let (mins, mins_cur) = try!(scan_integer(cur));
452 let mins_len = cur.slice_between(mins_cur).unwrap().len();
453
454 if mins_len != "mm".len() {
455 return Err(ScanError::syntax("expected time in `hh:mm:ss` format")
456 .add_offset(cur.byte_pos()));
457 }
458
459 match mins_cur.next_cp() {
460 Some((':', cur)) => date_split_sec(dur + try!(dur_mins(mins, 0.0)), cur),
461 _ => Err(ScanError::syntax("expected time in `hh:mm:ss` format")
462 .add_offset(cur.byte_pos()))
463 }
464 }
465
466 #[cfg(feature="duration-iso8601-dates")]
467 fn date_split_sec(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
468 let (secs, secs_cur) = try!(scan_integer(cur));
469 let secs_len = cur.slice_between(secs_cur).unwrap().len();
470
471 if secs_len != "ss".len() {
472 return Err(ScanError::syntax("expected time in `hh:mm:ss` format")
473 .add_offset(cur.byte_pos()));
474 }
475
476 Ok((dur + try!(dur_secs(secs, 0.0)), secs_cur))
477 }
478
479 macro_rules! add_dur {
480 ($a:expr, $b:expr) => {
481 try!(checked_add_dur($a, try!($b))
482 .ok_or_else(|| ScanError::other(MsgErr("duration overflowed"))))
483 };
484 }
485
486 #[cfg(feature="duration-iso8601-dates")]
487 fn given_year(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
488 match cur.next_cp() {
489 Some(('0'...'9', _)) => (),
490 Some(('T', cur)) => return given_date(dur, cur),
491 _ => return Ok((dur, cur))
492 }
493
494 let ((int, frac), cur) = try!(scan_real(cur));
495 match cur.next_cp() {
496 Some(('M', cur)) => given_month(add_dur!(dur, dur_months(int, frac)), cur),
497 Some(('D', cur)) => given_day(add_dur!(dur, dur_days(int, frac)), cur),
498 _ => Err(ScanError::syntax("expected number followed by one of `M`, or `D`")
499 .add_offset(cur.byte_pos()))
500 }
501 }
502
503 #[cfg(feature="duration-iso8601-dates")]
504 fn given_month(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
505 match cur.next_cp() {
506 Some(('0'...'9', _)) => (),
507 Some(('T', cur)) => return given_date(dur, cur),
508 _ => return Ok((dur, cur))
509 }
510
511 let ((int, frac), cur) = try!(scan_real(cur));
512 match cur.next_cp() {
513 Some(('D', cur)) => given_day(add_dur!(dur, dur_days(int, frac)), cur),
514 _ => Err(ScanError::syntax("expected number followed by `D`")
515 .add_offset(cur.byte_pos()))
516 }
517 }
518
519 #[cfg(feature="duration-iso8601-dates")]
520 fn given_day(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
521 match cur.next_cp() {
522 Some(('T', cur)) => given_date(dur, cur),
523 _ => Ok((dur, cur))
524 }
525 }
526
527 fn given_date(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
528 let ((int, frac), cur) = try!(scan_real(cur));
529 match cur.next_cp() {
530 Some(('H', cur)) => given_hour(add_dur!(dur, dur_hours(int, frac)), cur),
531 Some(('M', cur)) => given_min(add_dur!(dur, dur_mins(int, frac)), cur),
532 Some(('S', cur)) => given_sec(add_dur!(dur, dur_secs(int, frac)), cur),
533 _ => Err(ScanError::syntax("expected number followed by one of `H`, `M`, or `S`")
534 .add_offset(cur.byte_pos()))
535 }
536 }
537
538 fn given_hour(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
539 match cur.next_cp() {
540 Some(('0'...'9', _)) => (),
541 _ => return Ok((dur, cur))
542 }
543
544 let ((int, frac), cur) = try!(scan_real(cur));
545 match cur.next_cp() {
546 Some(('M', cur)) => given_min(add_dur!(dur, dur_mins(int, frac)), cur),
547 Some(('S', cur)) => given_sec(add_dur!(dur, dur_secs(int, frac)), cur),
548 _ => Err(ScanError::syntax("expected number followed by one of `M`, or `S`")
549 .add_offset(cur.byte_pos()))
550 }
551 }
552
553 fn given_min(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
554 match cur.next_cp() {
555 Some(('0'...'9', _)) => (),
556 _ => return Ok((dur, cur))
557 }
558
559 let ((int, frac), cur) = try!(scan_real(cur));
560 match cur.next_cp() {
561 Some(('S', cur)) => given_sec(add_dur!(dur, dur_secs(int, frac)), cur),
562 _ => Err(ScanError::syntax("expected number followed by `S`")
563 .add_offset(cur.byte_pos()))
564 }
565 }
566
567 fn given_sec(dur: Duration, cur: StrCursor) -> ScanResult<Duration, StrCursor> {
568 Ok((dur, cur))
569 }
570}
571
572fn checked_add_dur(a: Duration, b: Duration) -> Option<Duration> {
573 let (a_s, a_ns) = (a.as_secs(), a.subsec_nanos());
574 let (b_s, b_ns) = (b.as_secs(), b.subsec_nanos());
575 let c_ns = a_ns + b_ns;
576 let (c_ns, c_carry) = match c_ns {
577 c_ns if c_ns > NANOS_IN_SEC => (c_ns - NANOS_IN_SEC, 1),
578 c_ns => (c_ns, 0)
579 };
580 a_s.checked_add(b_s)
581 .and_then(|c_s| c_s.checked_add(c_carry))
582 .map(|c_s| Duration::new(c_s, c_ns))
583}
584
585macro_rules! dur_conv {
586 (
587 $($(#[$attrs:meta])* fn $fn_name:ident($name:expr, $scale:expr);)*
588 ) => {
589 $(
590 $(#[$attrs])*
591 fn $fn_name(int: u64, frac: f64) -> Result<Duration, ScanError> {
592 const MSG: &'static str = concat!("overflow converting ",
593 $name, " into seconds");
594 assert!(0.0f64 <= frac && frac < 1.0f64);
595 let secs = try!(int.checked_mul($scale)
596 .ok_or_else(|| ScanError::other(MsgErr(MSG))));
597
598 let nanos = frac * ($scale as f64);
599 let secs = try!(secs.checked_add(nanos as u64)
600 .ok_or_else(|| ScanError::other(MsgErr(MSG))));
601 let nanos = (nanos.fract() * (NANOS_IN_SEC as f64)) as u32;
602
603 Ok(Duration::new(secs, nanos))
604 }
605 )*
606 };
607}
608
609dur_conv! {
610 #[cfg(feature="duration-iso8601-dates")] fn dur_years("years", SECS_IN_YEAR);
611 #[cfg(feature="duration-iso8601-dates")] fn dur_months("months", SECS_IN_MONTH);
612 #[cfg(feature="duration-iso8601-dates")] fn dur_weeks("weeks", SECS_IN_WEEK);
613 #[cfg(feature="duration-iso8601-dates")] fn dur_days("days", SECS_IN_DAY);
614
615 fn dur_hours("hours", SECS_IN_HOUR);
616 fn dur_mins("mins", SECS_IN_MIN);
617 fn dur_secs("secs", SECS_IN_SEC);
618}
619
620fn scan_integer(cur: StrCursor) -> ScanResult<u64, StrCursor> {
621 let start = cur;
622 let mut cur = match cur.next_cp() {
623 Some(('0'...'9', cur)) => cur,
624 _ => return Err(ScanError::syntax("expected digit")
625 .add_offset(cur.byte_pos()))
626 };
627
628 loop {
629 cur = match cur.next_cp() {
630 Some(('0'...'9', cur)) => cur,
631 _ => {
632 let v = try!(start.slice_between(cur).unwrap().parse()
633 .map_err(|e| ScanError::int(e)
634 .add_offset(cur.byte_pos())));
635 return Ok((v, cur));
636 }
637 }
638 }
639}
640
641fn scan_real(cur: StrCursor) -> ScanResult<(u64, f64), StrCursor> {
649 let (int, cur) = try!(scan_integer(cur));
650 let cur = match cur.next_cp() {
651 Some(('.', cur)) | Some((',', cur)) => cur,
652 _ => return Ok(((int, 0.0), cur))
653 };
654 scan_real_frac(int, cur)
655}
656
657fn scan_real_frac(int: u64, cur: StrCursor) -> ScanResult<(u64, f64), StrCursor> {
658 let (frac, frac_cur) = try!(scan_integer(cur));
659 let frac_len = cur.slice_between(frac_cur).unwrap().len();
660 let frac = frac as f64;
661 let frac = frac / (10.0f64).powf(frac_len as f64);
662 Ok(((int, frac), frac_cur))
663}