ion_c_sys/result.rs
1// Copyright Amazon.com, Inc. or its affiliates.
2
3//! Provides convenient integration with `Error` and `Result` for Ion C.
4
5use crate::*;
6
7use std::error::Error;
8use std::ffi::CStr;
9use std::fmt;
10use std::num::TryFromIntError;
11
12/// IonC Error code and its associated error message.
13///
14/// `position` is populated when errors come from readers. See [`Position`] for
15/// more information.
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub struct IonCError {
18 pub code: i32,
19 pub message: &'static str,
20 pub additional: &'static str,
21 pub position: Position,
22}
23
24/// Represents a position in a data source. For example, consider a file
25/// containing Ion data that is being parsed using an [IonCReader](crate::reader::IonCReader).
26///
27/// If a position is set, `bytes` will always be hydrated while `lines` and
28/// `offset` will only be populated for text readers.
29#[derive(Copy, Clone, Debug, PartialEq, Eq)]
30pub enum Position {
31 Unknown,
32 Offset(i64),
33 OffsetLineColumn(i64, LineColumn),
34}
35
36// see above
37#[derive(Copy, Clone, Debug, PartialEq, Eq)]
38pub struct LineColumn(pub i32, pub i32);
39
40impl Position {
41 /// Make a new position based on Ion text data. `line` and `offset` are
42 /// known for text sources.
43 pub fn text(bytes: i64, line: i32, offset: i32) -> Position {
44 Position::OffsetLineColumn(bytes, LineColumn(line, offset))
45 }
46
47 /// Make a new position based on Ion binary data. Only the byte offset is
48 /// known.
49 pub fn binary(bytes: i64) -> Position {
50 Position::Offset(bytes)
51 }
52}
53
54impl IonCError {
55 /// Constructs an `IonCError` from an `iERR` error code.
56 pub fn from(code: i32) -> Self {
57 Self::with_additional(code, "iERR Result")
58 }
59
60 /// Constructs an `IonCError` from an `iERR` error code and its own message
61 pub fn with_additional(code: i32, additional: &'static str) -> Self {
62 match code {
63 ion_error_code_IERR_NOT_IMPL..=ion_error_code_IERR_INVALID_LOB_TERMINATOR => {
64 unsafe {
65 // this gives us static storage pointer so it doesn't violate lifetime
66 let c_str = CStr::from_ptr(ion_error_to_str(code));
67 // the error codes are all ASCII so a panic here is a bug
68 let message = c_str.to_str().unwrap();
69 Self {
70 code,
71 message,
72 additional,
73 position: Position::Unknown,
74 }
75 }
76 }
77 _ => Self {
78 code,
79 message: "Unknown Ion C Error Code",
80 additional,
81 position: Position::Unknown,
82 },
83 }
84 }
85
86 /// Adds a `Position` to an existing `IonCError`.
87 pub fn with_position(mut self, pos: Position) -> Self {
88 self.position = pos;
89 self
90 }
91}
92
93impl fmt::Display for IonCError {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(
96 f,
97 "Error {}: {} ({})",
98 self.code, self.message, self.additional
99 )
100 }
101}
102
103impl Error for IonCError {}
104
105impl From<TryFromIntError> for IonCError {
106 /// Due to the way Ion C works with sizes as i32, it is convenient to be able to coerce
107 /// a TryFromIntError to `IonCError`.
108 fn from(_: TryFromIntError) -> Self {
109 IonCError::from(ion_error_code_IERR_NUMERIC_OVERFLOW)
110 }
111}
112
113impl From<Utf8Error> for IonCError {
114 /// Due to the way Ion C works with raw UTF-8 byte sequences, it is convenient to be able
115 /// to coerce a `Utf8Error` to `IonCError`.
116 fn from(_: Utf8Error) -> Self {
117 IonCError::from(ion_error_code_IERR_INVALID_UTF8)
118 }
119}
120
121/// A type alias to results from Ion C API, the result value is generally `()` to signify
122/// `ion_error_code_IERR_OK` since Ion C doesn't return results but generally takes
123/// output parameters.
124pub type IonCResult<T> = Result<T, IonCError>;
125
126/// Macro to transform Ion C error code expressions into `Result<(), IonCError>`.
127/// Higher-level facades over Ion C functions could map this to `Result<T, IonCError>`
128/// or the like.
129///
130/// NB: `ionc!` implies `unsafe` code.
131///
132/// ## Usage
133///
134/// ```
135/// # use std::ptr;
136/// # use ion_c_sys::*;
137/// # use ion_c_sys::result::*;
138/// # fn main() -> IonCResult<()> {
139/// let mut data = String::from("42");
140/// let mut ion_reader: hREADER = ptr::null_mut();
141/// let mut ion_type: ION_TYPE = ptr::null_mut();
142/// ionc!(
143/// ion_reader_open_buffer(
144/// &mut ion_reader,
145/// data.as_mut_ptr(),
146/// data.len() as i32,
147/// ptr::null_mut()
148/// )
149/// )?;
150///
151/// ionc!(ion_reader_next(ion_reader, &mut ion_type))?;
152/// assert_eq!(ion_type as u32, tid_INT_INT);
153///
154/// let mut value = 0;
155/// ionc!(ion_reader_read_int64(ion_reader, &mut value))?;
156/// assert_eq!(value, 42);
157///
158/// ionc!(ion_reader_close(ion_reader))
159/// # }
160/// ```
161#[macro_export]
162macro_rules! ionc {
163 ($e:expr) => {
164 unsafe {
165 let err: i32 = $e;
166 match err {
167 $crate::ion_error_code_IERR_OK => Ok(()),
168 code => Err($crate::result::IonCError::from(code)),
169 }
170 }
171 };
172}