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}