1#![allow(non_upper_case_globals)]
116#![allow(non_camel_case_types)]
117#![allow(non_snake_case)]
118
119pub mod decimal;
120pub mod int;
121pub mod reader;
122pub mod result;
123pub mod string;
124pub mod timestamp;
125pub mod writer;
126
127include!(concat!(env!("OUT_DIR"), "/ionc_bindings.rs"));
128
129use crate::decimal::IonDecimalPtr;
130use crate::int::IonIntPtr;
131use crate::result::*;
132use crate::timestamp::Mantissa::*;
133use crate::timestamp::TSOffsetKind::*;
134use crate::timestamp::TSPrecision::*;
135use crate::timestamp::{IonDateTime, Mantissa, TSOffsetKind, TS_MAX_MANTISSA_DIGITS};
136
137use std::cmp::min;
138use std::convert::TryInto;
139use std::marker::PhantomData;
140use std::ptr;
141use std::str::Utf8Error;
142use std::{slice, str};
143
144use bigdecimal::{BigDecimal, ToPrimitive, Zero};
145use chrono::offset::FixedOffset;
146use chrono::{Datelike, LocalResult, TimeZone, Timelike};
147use num_bigint::{BigInt, Sign};
148use paste::paste;
149
150#[cfg(test)]
151use rstest_reuse;
152
153impl ION_INT {
154 pub fn try_assign_bigint(&mut self, src: &BigInt) -> IonCResult<()> {
160 let (sign, mut raw_mag) = src.to_bytes_be();
161 let is_neg = match sign {
162 Sign::Minus => 1,
163 _ => 0,
164 };
165
166 ionc!(ion_int_from_abs_bytes(
167 &mut *self,
168 raw_mag.as_mut_ptr(),
169 raw_mag.len().try_into()?,
170 is_neg
171 ))?;
172
173 Ok(())
174 }
175
176 pub fn try_to_bigint(&self) -> IonCResult<BigInt> {
178 if self._digits.is_null() {
179 return Err(IonCError::from(ion_error_code_IERR_NULL_VALUE));
180 }
181 if self._len < 0 {
182 return Err(IonCError::from(ion_error_code_IERR_INVALID_ARG));
183 }
184 if self._signum < -1 || self._signum > 1 {
185 return Err(IonCError::from(ion_error_code_IERR_INVALID_ARG));
186 }
187 let src_digits = unsafe { slice::from_raw_parts(self._digits, self._len.try_into()?) };
188
189 const ION_INT_BITS: u64 = 31;
192 const BIGINT_BITS: u64 = 32;
193 const ION_INT_DIGIT_MASK: u64 = 0x7FFF_FFFF;
194 let tgt_len = (((self._len as u64) * ION_INT_BITS) / BIGINT_BITS) + 1;
195 let mut digits = vec![0u32; tgt_len.try_into()?];
196
197 let mut bits_written = 0u64;
199 for src_digit in src_digits.iter().rev() {
202 let src_digit = (*src_digit as u64) & ION_INT_DIGIT_MASK;
204 let tgt_idx = (bits_written >> 5) as usize;
206 let filled_bits = bits_written & 0x1F;
208 let avail_bits = BIGINT_BITS - filled_bits;
210 let rem_bits = ION_INT_BITS - min(ION_INT_BITS, avail_bits);
212
213 let old_tgt_digit = digits[tgt_idx];
215 let high_bit_mask = (src_digit << filled_bits) as u32;
216 let new_tgt_digit = old_tgt_digit | high_bit_mask;
217 digits[tgt_idx] = new_tgt_digit;
218
219 if tgt_idx + 1 < digits.len() && rem_bits > 0 {
220 let next_idx = tgt_idx + 1;
222 let shift_bits = ION_INT_BITS - rem_bits;
223 let next_tgt_digit = (src_digit >> shift_bits) as u32;
224 digits[next_idx] = next_tgt_digit;
225 }
226
227 bits_written += ION_INT_BITS as u64;
228 }
229
230 const SIGN_TABLE: &[Sign] = &[Sign::Minus, Sign::NoSign, Sign::Plus];
231 Ok(BigInt::new(SIGN_TABLE[(self._signum + 1) as usize], digits))
232 }
233}
234
235#[cfg(test)]
236mod test_bigint {
237 use crate::int::*;
238 use crate::result::*;
239 use crate::*;
240
241 use rstest::rstest;
242 use rstest_reuse::{self, *};
243
244 use num_bigint::BigInt;
245 use num_bigint::Sign::{self, *};
246
247 #[template]
250 #[rstest(
251 lit,
252 sign,
253 case::zero("0", NoSign),
254 case::pos_31_bit("1576217826", Plus),
255 case::neg_31_bit("-1135682218", Minus),
256 case::pos_62_bit("4044881356853627201", Plus),
257 case::neg_62_bit("-3912230224800585615", Minus),
258 case::pos_80_bit("739079489563988370954567", Plus),
259 case::neg_80_bit("-1086195751445330490038795", Minus),
260 case::pos_256_bit(
261 "137867910096739512996847672171101012368076859213341045932878406344693462874820",
262 Plus,
263 ),
264 case::neg_256_bit(
265 "-172272298565065214306566076919200322665607032158922187439565911507697602517448",
266 Minus,
267 ),
268 case::pos_280_bit(
269 "1757357796823956205198798709416201514711937158830789249081025568737706527211427788829",
270 Plus,
271 ),
272 case::neg_280_bit(
273 "-1075268761612498909802747877455511969232059561308078408290306546278351574885791689247",
274 Minus,
275 )
276 )]
277 fn bigint(lit: &str, sign: Sign) {}
278
279 #[apply(bigint)]
280 fn try_assign_bigint(lit: &str, sign: Sign) -> IonCResult<()> {
281 let big_val = BigInt::parse_bytes(lit.as_bytes(), 10).unwrap();
282 let mut ion_val = IonIntPtr::try_from_bigint(&big_val)?;
283
284 let mut buf = vec![0u8; 512];
285 let mut len = 0;
286 ionc!(ion_int_to_char(
287 ion_val.as_mut_ptr(),
288 buf.as_mut_ptr(),
289 buf.len().try_into()?,
290 &mut len
291 ))?;
292
293 let expected_signum = match sign {
294 Minus => -1,
295 NoSign => 0,
296 Plus => 1,
297 };
298 let mut actual_signum = 0;
299 ionc!(ion_int_signum(ion_val.as_mut_ptr(), &mut actual_signum))?;
300 assert_eq!(expected_signum, actual_signum);
301 assert_eq!(lit.as_bytes(), &buf[0..len.try_into()?]);
302
303 Ok(())
304 }
305
306 #[apply(bigint)]
307 fn try_to_bigint(lit: &str, sign: Sign) -> IonCResult<()> {
308 let mut ion_val = IonIntPtr::try_new()?;
309
310 let mut ion_str = ION_STRING::try_from_str(lit)?;
311 ionc!(ion_int_from_string(ion_val.as_mut_ptr(), &mut ion_str))?;
312
313 let big_val = ion_val.try_to_bigint()?;
314 assert_eq!(sign, big_val.sign());
315 assert_eq!(lit, big_val.to_string().as_str());
316
317 Ok(())
318 }
319}
320
321#[inline]
326fn make_context() -> decContext {
327 let mut ctx = decContext::default();
328 unsafe { decContextDefault(&mut ctx, DEC_INIT_DECQUAD as i32) };
329 ctx.digits = DEC_MAX_DIGITS as i32;
330 ctx.emax = DEC_MAX_EMAX as i32;
331 ctx.emin = DEC_MIN_EMIN as i32;
332 ctx.clamp = 0;
334
335 ctx
336}
337
338const DEC_NUMBER_ERROR_MSG: &str = "DecNumber Not Supported";
340
341impl ION_DECIMAL {
342 pub fn try_assign_bigdecimal(&mut self, value: &BigDecimal) -> IonCResult<()> {
344 let digits = value.digits();
345
346 ionc!(ion_decimal_free(self))?;
348 if digits > (DECQUAD_Pmax as u64) {
349 self.type_ = ION_DECIMAL_TYPE_ION_DECIMAL_TYPE_NUMBER;
350 ionc!(_ion_decimal_number_alloc(
352 ptr::null_mut(),
353 digits.try_into()?,
354 &mut self.value.num_value,
355 ))?;
356 } else {
357 self.type_ = ION_DECIMAL_TYPE_ION_DECIMAL_TYPE_QUAD;
358 }
359 ionc!(ion_decimal_zero(self))?;
360
361 let mut ctx = make_context();
362 let (coefficient, scale) = value.as_bigint_and_exponent();
363
364 let mut ion_coefficient = IonIntPtr::try_from_bigint(&coefficient)?;
366 ionc!(ion_decimal_from_ion_int(
367 self,
368 &mut ctx,
369 ion_coefficient.as_mut_ptr()
370 ))?;
371
372 let mut dec_scale = IonDecimalPtr::try_from_i32((-scale).try_into()?)?;
374 ionc!(ion_decimal_scaleb(
375 self,
376 self,
377 dec_scale.as_mut_ptr(),
378 &mut ctx
379 ))?;
380
381 match unsafe { decContextGetStatus(&mut ctx) } {
382 0 => Ok(()),
383 DEC_Invalid_context => Err(IonCError::with_additional(
385 ion_error_code_IERR_INVALID_STATE,
386 DEC_NUMBER_ERROR_MSG,
387 )),
388 _ => Err(IonCError::from(ion_error_code_IERR_INTERNAL_ERROR)),
389 }
390 }
391
392 pub fn try_to_bigdecimal(&mut self) -> IonCResult<BigDecimal> {
401 let special =
403 unsafe { ion_decimal_is_nan(self) } | unsafe { ion_decimal_is_infinite(self) };
404 if special != 0 {
405 return Err(IonCError::from(ion_error_code_IERR_INVALID_ARG));
406 }
407
408 let exponent = unsafe { ion_decimal_get_exponent(self) };
410 let mut ctx = make_context();
411
412 let mut scale_amount = IonDecimalPtr::try_from_i32(-exponent)?;
414 ionc!(ion_decimal_scaleb(
415 self,
416 self,
417 scale_amount.as_mut_ptr(),
418 &mut ctx
419 ))?;
420 let mut ion_coefficient = IonIntPtr::try_new()?;
421 ionc!(ion_decimal_to_ion_int(
422 self,
423 &mut ctx,
424 ion_coefficient.as_mut_ptr()
425 ))?;
426
427 ionc!(ion_decimal_from_int32(scale_amount.as_mut_ptr(), exponent))?;
429 ionc!(ion_decimal_scaleb(
430 self,
431 self,
432 scale_amount.as_mut_ptr(),
433 &mut ctx
434 ))?;
435
436 let coefficient = ion_coefficient.try_to_bigint()?;
438 Ok(BigDecimal::new(coefficient, -exponent as i64))
439 }
440}
441
442#[cfg(test)]
443mod test_bigdecimal {
444 #![allow(unused_variables)]
446
447 use crate::decimal::*;
448 use crate::result::*;
449 use crate::*;
450
451 use rstest::rstest;
452 use rstest_reuse::{self, *};
453
454 use std::ffi::CString;
455
456 use bigdecimal::BigDecimal;
457 use num_bigint::BigInt;
458 use std::os::raw::c_char;
459
460 #[template]
463 #[rstest(
464 d_lit, c_lit, exponent,
465 case::zero("0E0", "0", 0),
466 case::zero_p1_en8("0E-8", "0", -8),
467 case::n8_en9(
468 "-0.060231219",
469 "-60231219",
470 -9
471 ),
472 case::p8_en9(
473 "0.060231219",
474 "60231219",
475 -9
476 ),
477 case::n8_e7(
478 "-2.7851880E+14",
479 "-27851880",
480 7
481 ),
482 case::p8_e7(
483 "2.7851880E+14",
484 "27851880",
485 7
486 ),
487 case::n10_en32(
488 "-8.960115983E-23",
489 "-8960115983",
490 -32
491 ),
492 case::p10_en32(
493 "8.960115983E-23",
494 "8960115983",
495 -32
496 ),
497 case::n10_e33(
498 "-9.020634788E+42",
499 "-9020634788",
500 33
501 ),
502 case::p10_e33(
503 "9.020634788E+42",
504 "9020634788",
505 33
506 ),
507 case::p28_en200(
508 "6.262354479103128947759990609E-173",
509 "6262354479103128947759990609",
510 -200
511 ),
512 case::p28_e256(
513 "9.486968202420944975464220485E+283",
514 "9486968202420944975464220485",
515 256
516 ),
517 case::p30_en80(
518 "1.95671174876514167707949046494E-51",
519 "195671174876514167707949046494",
520 -80
521 ),
522 case::p30_e85(
523 "6.50276908237082165030429776240E+114",
524 "650276908237082165030429776240",
525 85
526 ),
527 case::p32_en2500(
528 "4.1111441587902074230255158471962E-2469",
529 "41111441587902074230255158471962",
530 -2500
531 ),
532 case::p32_e2000(
533 "2.9557665423302931520009385209142E+2031",
534 "29557665423302931520009385209142",
535 2000
536 ),
537 case::p34_en6100(
538 "7.550261799183309871071383313529625E-6067",
539 "7550261799183309871071383313529625",
540 -6100
541 ),
542 case::p34_e6110(
543 "6.385180511995820599706505142429397E+6143",
544 "6385180511995820599706505142429397",
545 6110
546 ),
547 case::p35_en5500(
548 "5.7516904150035820771702738217456585E-5466",
549 "57516904150035820771702738217456585",
550 -5500
551 ),
552 case::p35_e4500(
553 "7.7008733801862767698341856677462573E+4534",
554 "77008733801862767698341856677462573",
555 4500
556 ),
557 case::p37_en5000(
558 "4.623874712756984956766514373293465450E-4964",
559 "4623874712756984956766514373293465450",
560 -5000
561 ),
562 case::p37_e6000(
563 "9.970304500552494301196940956407522192E+6036",
564 "9970304500552494301196940956407522192",
565 6000
566 ),
567 case::p38_en110(
568 "5.9025723530133359201873978774331457987E-73",
569 "59025723530133359201873978774331457987",
570 -110
571 ),
572 case::p38_e120(
573 "1.9141033431585215614236049655094977149E+157",
574 "19141033431585215614236049655094977149",
575 120
576 ),
577 case::p80_en100(
578 "4.4818084368071883463971983799827359329516552715395136426901699171061657459862827E-21",
579 "44818084368071883463971983799827359329516552715395136426901699171061657459862827",
580 -100
581 ),
582 case::p80_e100(
583 "1.1050923646496935500303958597719760541602476378798913802309108901778807590265278E+279",
584 "11050923646496935500303958597719760541602476378798913802309108901778807590265278",
585 200
586 ),
587 case::p90_en7500(
588 "4.29926815238794553771012929776377470059985366468509955296419658127318674237363065734779169E-7411",
589 "429926815238794553771012929776377470059985366468509955296419658127318674237363065734779169",
590 -7500
591 ),
592 case::p95_e8900(
593 "7.2528362571947544011741381371920928164665526193613163529470013995124245887784236337589645597617E+8994",
594 "72528362571947544011741381371920928164665526193613163529470013995124245887784236337589645597617",
595 8900
596 ),
597 )]
598 fn bigdecimal(d_lit: &str, c_lit: &str, exponent: i32) {}
599
600 #[apply(bigdecimal)]
601 fn try_assign_bigdecimal(d_lit: &str, c_lit: &str, exponent: i32) -> IonCResult<()> {
602 let big_val = BigDecimal::parse_bytes(d_lit.as_bytes(), 10).unwrap();
603 match IonDecimalPtr::try_from_bigdecimal(&big_val) {
604 Ok(mut ion_val) => {
605 let actual_exponent = unsafe { ion_decimal_get_exponent(ion_val.as_mut_ptr()) };
606
607 let mut buf = vec![0u8; 128usize];
609 ionc!(ion_decimal_to_string(
610 ion_val.as_mut_ptr(),
611 buf.as_mut_ptr() as *mut c_char
612 ))?;
613 let len = unsafe { strlen(buf.as_ptr() as *const c_char) };
614 assert_eq!(
615 d_lit.replace("E", "d"),
616 str::from_utf8(&buf[0..len.try_into()?]).unwrap(),
617 "Testing string serialization from ION_DECIMAL"
618 );
619 assert_eq!(exponent, actual_exponent, "Testing exponents");
620 }
621 Err(e) => match e.code {
622 ion_error_code_IERR_INVALID_STATE => match e.additional {
623 DEC_NUMBER_ERROR_MSG => println!("Ignoring amzn/ion-rust#79 for {}", d_lit),
624 _ => assert!(false, "Unexpected error {:?}", e),
625 },
626 _ => assert!(false, "Unexpected error: {:?}", e),
627 },
628 }
629
630 Ok(())
631 }
632
633 #[apply(bigdecimal)]
634 fn try_to_bigdecimal(d_lit: &str, c_lit: &str, exponent: i32) -> IonCResult<()> {
635 let cstring = CString::new(d_lit).unwrap();
636 let mut ctx = make_context();
637 let mut ion_val = IonDecimalPtr::try_from_existing(ION_DECIMAL::default())?;
638 ionc!(ion_decimal_from_string(
639 ion_val.as_mut_ptr(),
640 cstring.as_ptr(),
641 &mut ctx
642 ))?;
643
644 let big_val = ion_val.try_to_bigdecimal()?;
645 assert_eq!(
646 big_val,
647 ion_val.try_to_bigdecimal()?,
648 "Make sure conversion is effectively immutable"
649 );
650
651 let expected_coefficient = BigInt::parse_bytes(c_lit.as_bytes(), 10).unwrap();
654 let (actual_coefficient, scale) = big_val.as_bigint_and_exponent();
655 assert_eq!(
656 expected_coefficient, actual_coefficient,
657 "Testing coefficents"
658 );
659 assert_eq!(exponent, (-scale).try_into()?, "Testing exponents");
660
661 Ok(())
662 }
663}
664
665const SEC_IN_MINS: i32 = 60;
666const NS_IN_SEC: u32 = 1_000_000_000;
667
668impl ION_TIMESTAMP {
669 pub fn try_assign_from_iondt(&mut self, ion_dt: &IonDateTime) -> IonCResult<()> {
675 let dt = ion_dt.as_datetime();
676 let prec = ion_dt.precision();
677 let offset_kind = ion_dt.offset_kind();
678
679 self.month = 1;
681 self.day = 1;
682 self.hours = 0;
683 self.minutes = 0;
684 self.seconds = 0;
685 unsafe {
686 decQuadZero(&mut self.fraction);
687 }
688 self.tz_offset = 0;
689 self.precision = 0;
690
691 self.year = dt.year().try_into()?;
693 if *prec >= Month {
694 self.month = dt.month().try_into()?;
695 if *prec >= Day {
696 self.day = dt.day().try_into()?;
697 if *prec >= Minute {
698 self.hours = dt.hour().try_into()?;
699 self.minutes = dt.minute().try_into()?;
700 if *prec >= Second {
701 self.seconds = dt.second().try_into()?;
702 if let Fractional(mantissa) = prec {
703 self.try_assign_mantissa(dt, mantissa)?;
704 }
705 }
706 }
707 }
708 }
709
710 self.precision = match prec {
712 Year => ION_TS_YEAR,
713 Month => ION_TS_MONTH,
714 Day => ION_TS_DAY,
715 Minute => ION_TS_MIN,
716 Second => ION_TS_SEC,
717 Fractional(_) => ION_TS_FRAC,
718 } as u8;
719
720 match offset_kind {
722 KnownOffset => {
723 let offset_minutes = dt.offset().local_minus_utc() / SEC_IN_MINS;
724 ionc!(ion_timestamp_set_local_offset(self, offset_minutes))?;
725 }
726 UnknownOffset => {
727 ionc!(ion_timestamp_unset_local_offset(self))?;
728 }
729 }
730
731 Ok(())
732 }
733
734 fn try_assign_mantissa<T>(&mut self, dt: &T, mantissa: &Mantissa) -> IonCResult<()>
736 where
737 T: Timelike,
738 {
739 let mut ctx = make_context();
740 match mantissa {
741 Digits(digits) => {
742 unsafe {
743 let nanos = dt.nanosecond();
744 decQuadFromUInt32(&mut self.fraction, nanos);
745
746 let mut shift = decQuad::default();
748 decQuadFromInt32(
749 &mut shift,
750 (*digits as i32) - (TS_MAX_MANTISSA_DIGITS as i32),
751 );
752 decQuadShift(&mut self.fraction, &self.fraction, &shift, &mut ctx);
753
754 let mut scale = decQuad::default();
756 decQuadFromInt32(&mut scale, -(*digits as i32));
757 decQuadScaleB(&mut self.fraction, &self.fraction, &scale, &mut ctx);
758 }
759 }
760 Fraction(frac) => {
761 let ion_frac = IonDecimalPtr::try_from_bigdecimal(frac)?;
762 if ion_frac.type_ != ION_DECIMAL_TYPE_ION_DECIMAL_TYPE_QUAD {
764 return Err(IonCError::with_additional(
765 ion_error_code_IERR_INVALID_TIMESTAMP,
766 "Precision exceeds decQuad for Timestamp",
767 ));
768 }
769 unsafe {
770 self.fraction = (*ion_frac).value.quad_value;
771 }
772 }
773 }
774
775 Ok(())
776 }
777
778 pub fn try_to_iondt(&mut self) -> IonCResult<IonDateTime> {
784 let mut prec = 0;
785 ionc!(ion_timestamp_get_precision(self, &mut prec))?;
786 let prec = prec as u32;
788
789 let mut offset_minutes = 0;
791 let mut has_offset = 0;
792
793 if prec >= ION_TS_MIN {
794 ionc!(ion_timestamp_has_local_offset(self, &mut has_offset))?;
795 if has_offset != 0 {
796 ionc!(ion_timestamp_get_local_offset(self, &mut offset_minutes))?;
797 }
798 }
799 let offset_seconds = (offset_minutes as i32) * SEC_IN_MINS;
800 let tz = FixedOffset::east_opt(offset_seconds)
801 .ok_or_else(|| IonCError::from(ion_error_code_IERR_INVALID_STATE))?;
802 let ts_offset = if has_offset != 0 {
803 TSOffsetKind::KnownOffset
804 } else {
805 TSOffsetKind::UnknownOffset
806 };
807
808 let day = if prec >= ION_TS_DAY { self.day } else { 1 };
809 let month = if prec >= ION_TS_MONTH { self.month } else { 1 };
810 match tz.ymd_opt(self.year as i32, month as u32, day as u32) {
811 LocalResult::Single(date) => {
812 let mut hours = 0;
813 let mut minutes = 0;
814 let mut seconds = 0;
815 if prec >= ION_TS_MIN {
816 hours = self.hours;
817 minutes = self.minutes;
818 seconds = self.seconds;
819 }
820
821 let frac_seconds = if prec >= ION_TS_FRAC {
823 IonDecimalPtr::try_from_decquad(self.fraction)?.try_to_bigdecimal()?
824 } else {
825 BigDecimal::zero()
826 };
827 if frac_seconds < BigDecimal::zero() || frac_seconds >= BigDecimal::from(1) {
828 return Err(IonCError::from(ion_error_code_IERR_INVALID_STATE));
829 }
830 let nanos = (&frac_seconds * BigDecimal::from(NS_IN_SEC))
831 .abs()
832 .to_u32()
833 .unwrap();
834
835 let dt = date
836 .and_hms_nano_opt(hours as u32, minutes as u32, seconds as u32, nanos)
837 .ok_or_else(|| {
838 IonCError::with_additional(
839 ion_error_code_IERR_INVALID_TIMESTAMP,
840 "Could not create DateTime",
841 )
842 })?;
843
844 let ts_prec = match prec {
845 ION_TS_YEAR => Year,
846 ION_TS_MONTH => Month,
847 ION_TS_DAY => Day,
848 ION_TS_MIN => Minute,
849 ION_TS_SEC => Second,
850 ION_TS_FRAC => {
851 let (_, exponent) = frac_seconds.as_bigint_and_exponent();
852 let mantissa = if exponent <= TS_MAX_MANTISSA_DIGITS {
853 Digits(exponent as u32)
854 } else {
855 Fraction(frac_seconds)
857 };
858 Fractional(mantissa)
859 }
860 _ => {
861 return Err(IonCError::with_additional(
862 ion_error_code_IERR_INVALID_TIMESTAMP,
863 "Invalid ION_TIMESTAMP precision",
864 ))
865 }
866 };
867
868 Ok(IonDateTime::new(dt, ts_prec, ts_offset))
869 }
870 _ => Err(IonCError::with_additional(
871 ion_error_code_IERR_INVALID_TIMESTAMP,
872 "Could not create Date",
873 )),
874 }
875 }
876}
877
878#[cfg(test)]
879mod test_timestamp {
880 use crate::result::*;
881 use crate::timestamp::*;
882 use crate::*;
883
884 use rstest::rstest;
885 use rstest_reuse::{self, *};
886
887 use std::ffi::CString;
888 use std::os::raw::c_char;
889 use std::str;
890
891 use chrono::DateTime;
892
893 fn frac(lit: &str) -> Mantissa {
894 Fraction(BigDecimal::parse_bytes(lit.as_bytes(), 10).unwrap())
895 }
896
897 #[template]
898 #[rstest(
899 ion_lit,
900 iso_lit,
901 precision,
902 offset_kind,
903 case::ts_2020("2020T", "2020-01-01T00:00:00Z", Year, UnknownOffset),
904 case::ts_1901_04("1901-04T", "1901-04-01T00:00:00Z", Month, UnknownOffset),
905 case::ts_2014_10_10("2014-10-10", "2014-10-10T00:00:00Z", Day, UnknownOffset),
906 case::ts_2017_07_07T01_20(
907 "2017-07-07T01:20-00:00",
908 "2017-07-07T01:20:00Z",
909 Minute,
910 UnknownOffset,
911 ),
912 case::ts_2018_06_30T03_25_p07_30(
913 "2018-06-30T03:25+07:30",
914 "2018-06-30T03:25:00+07:30",
915 Minute,
916 KnownOffset,
917 ),
918 case::ts_2019_04_30T03_25_32(
919 "2019-04-30T03:25:32-00:00",
920 "2019-04-30T03:25:32Z",
921 Second,
922 UnknownOffset,
923 ),
924 case::ts_2016_03_30T03_25_32_n00_00_same_as_zulu(
925 "2016-03-30T03:25:32-00:00",
926 "2016-03-30T03:25:32Z",
927 Second,
928 UnknownOffset,
929 ),
930 case::ts_2016_03_30T03_25_32_n08_00(
931 "2016-03-30T03:25:32-08:00",
932 "2016-03-30T03:25:32-08:00",
933 Second,
934 KnownOffset,
935 ),
936 case::ts_1975_02_19T12_43_55_123_n08_00(
937 "1975-02-19T12:43:55.123-08:00",
938 "1975-02-19T12:43:55.123-08:00",
939 Fractional(Digits(3)),
940 KnownOffset,
941 ),
942 case::ts_1975_02_19T12_43_55_001_n08_00(
943 "1975-02-19T12:43:55.001-08:00",
944 "1975-02-19T12:43:55.001-08:00",
945 Fractional(Digits(3)),
946 KnownOffset,
947 ),
948 case::ts_1975_02_19T12_43_55_12345_n08_00(
949 "1975-02-19T12:43:55.12345-08:00",
950 "1975-02-19T12:43:55.12345-08:00",
951 Fractional(Digits(5)),
952 KnownOffset,
953 ),
954 case::ts_1975_02_19T12_43_55_00005_n08_00(
955 "1975-02-19T12:43:55.00005-08:00",
956 "1975-02-19T12:43:55.00005-08:00",
957 Fractional(Digits(5)),
958 KnownOffset,
959 ),
960 case::ts_1975_02_19T12_43_55_012345_n08_00(
961 "1975-02-19T12:43:55.012345-08:00",
962 "1975-02-19T12:43:55.012345-08:00",
963 Fractional(Digits(6)),
964 KnownOffset,
965 ),
966 case::ts_1975_02_19T12_43_55_000056_n08_00(
967 "1975-02-19T12:43:55.000056-08:00",
968 "1975-02-19T12:43:55.000056-08:00",
969 Fractional(Digits(6)),
970 KnownOffset,
971 ),
972 case::ts_1974_01_20T12_43_55_123456789_n08_00(
973 "1974-01-20T12:43:55.123456789-08:00",
974 "1974-01-20T12:43:55.123456789-08:00",
975 Fractional(Digits(9)),
976 KnownOffset,
977 ),
978 case::ts_1974_01_20T12_43_55_000056789_n08_00(
979 "1974-01-20T12:43:55.000056789-08:00",
980 "1974-01-20T12:43:55.000056789-08:00",
981 Fractional(Digits(9)),
982 KnownOffset,
983 ),
984 case::ts_1973_12_25T12_43_55_123456789123456789_n08_00_truncation(
985 "1973-12-25T12:43:55.123456789123456789-08:00",
986 "1973-12-25T12:43:55.123456789-08:00",
987 Fractional(frac("0.123456789123456789")),
988 KnownOffset,
989 )
990 )]
991 fn timestamp(ion_lit: &str, iso_lit: &str, precision: TSPrecision, offset_kind: TSOffsetKind) {}
992
993 #[apply(timestamp)]
994 fn assign_from_datetime(
995 ion_lit: &str,
996 iso_lit: &str,
997 precision: TSPrecision,
998 offset_kind: TSOffsetKind,
999 ) -> IonCResult<()> {
1000 let dt = DateTime::parse_from_rfc3339(iso_lit).unwrap();
1002 let ion_dt = IonDateTime::try_new(dt, precision, offset_kind)?;
1003 let mut ion_timestamp = ION_TIMESTAMP::default();
1004 ion_timestamp.try_assign_from_iondt(&ion_dt)?;
1005
1006 let mut ctx = make_context();
1008 let mut buf = vec![0u8; 128usize];
1009 let mut read = 0;
1010 ionc!(ion_timestamp_to_string(
1011 &mut ion_timestamp,
1012 buf.as_mut_ptr() as *mut c_char,
1013 buf.len().try_into()?,
1014 &mut read,
1015 &mut ctx
1016 ))?;
1017 assert_eq!(
1018 ion_lit,
1019 str::from_utf8(&buf[0..read.try_into()?])?,
1020 "Compare timestamp text serialization"
1021 );
1022
1023 Ok(())
1024 }
1025
1026 #[apply(timestamp)]
1027 fn try_to_datetime(
1028 ion_lit: &str,
1029 iso_lit: &str,
1030 precision: TSPrecision,
1031 offset_kind: TSOffsetKind,
1032 ) -> IonCResult<()> {
1033 let c_ion_lit_owned = CString::new(ion_lit).unwrap();
1034 let c_ion_lit = c_ion_lit_owned.as_bytes_with_nul();
1035 let mut ion_timestamp = ION_TIMESTAMP::default();
1037 let mut read = 0;
1038 let mut ctx = make_context();
1039 ionc!(ion_timestamp_parse(
1040 &mut ion_timestamp,
1041 c_ion_lit.as_ptr() as *mut c_char,
1042 c_ion_lit.len().try_into()?,
1043 &mut read,
1044 &mut ctx,
1045 ))?;
1046 assert_eq!(
1047 ion_lit.len(),
1048 read.try_into()?,
1049 "Test that we parsed the entire Ion timestamp literal"
1050 );
1051
1052 let expected_dt = DateTime::parse_from_rfc3339(iso_lit).unwrap();
1053 let ion_dt = ion_timestamp.try_to_iondt()?;
1054 assert_eq!(
1055 &expected_dt,
1056 ion_dt.as_datetime(),
1057 "Test that our converted timestamp is equivalent"
1058 );
1059 assert_eq!(
1060 &precision,
1061 ion_dt.precision(),
1062 "Compare timestamp precision"
1063 );
1064 assert_eq!(offset_kind, ion_dt.offset_kind(), "Compare offset kind");
1065
1066 Ok(())
1067 }
1068}
1069
1070impl ION_STRING {
1071 #[inline]
1091 pub fn try_from_mut_str(src: &mut str) -> IonCResult<Self> {
1092 unsafe { Self::try_from_mut_bytes(src.as_bytes_mut()) }
1093 }
1094
1095 #[inline]
1099 fn try_from_str(src: &str) -> IonCResult<Self> {
1100 Ok(Self {
1101 value: src.as_ptr() as *mut u8,
1102 length: src.len().try_into()?,
1103 })
1104 }
1105
1106 #[inline]
1123 pub fn try_from_mut_bytes(src: &mut [u8]) -> IonCResult<Self> {
1124 Ok(ION_STRING {
1125 value: src.as_mut_ptr(),
1126 length: src.len().try_into()?,
1127 })
1128 }
1129
1130 #[inline]
1153 pub fn try_as_str(&self) -> IonCResult<&str> {
1154 Ok(str::from_utf8(self.try_as_bytes()?)?)
1155 }
1156
1157 #[inline]
1169 pub fn try_as_bytes<'a>(&'a self) -> IonCResult<&'a [u8]> {
1170 self.as_bytes(PhantomData::<&'a u8>::default())
1171 }
1172
1173 fn as_str<'a>(&self, life: PhantomData<&'a u8>) -> IonCResult<&'a str> {
1175 unsafe {
1176 let raw_slice: &'a [u8] = self.as_bytes(life)?;
1177 let str_slice = str::from_utf8_unchecked(raw_slice);
1179 Ok(str_slice)
1180 }
1181 }
1182
1183 fn as_bytes<'a>(&self, _life: PhantomData<&'a u8>) -> IonCResult<&'a [u8]> {
1185 if self.value.is_null() {
1188 Err(IonCError::from(ion_error_code_IERR_NULL_VALUE))
1189 } else {
1190 unsafe {
1191 let u8_slice = slice::from_raw_parts(self.value, self.length.try_into()?);
1192 Ok(u8_slice)
1193 }
1194 }
1195 }
1196}
1197
1198macro_rules! ion_types {
1207 ( $($name:ident),* ) => {
1208 $(
1209 paste! {
1210 pub const [<ION_TYPE_ $name:upper>]: *mut ion_type =
1211 [<tid_ $name _INT>] as *mut ion_type;
1212 }
1213 )*
1214 };
1215}
1216
1217ion_types!(
1218 none, EOF, NULL, BOOL, INT, FLOAT, DECIMAL, TIMESTAMP, SYMBOL, STRING, CLOB, BLOB, LIST, SEXP,
1219 STRUCT, DATAGRAM
1220);