yyjson_rs/
read.rs

1use std::ffi::CStr;
2
3use num_derive::FromPrimitive;
4
5use yyjson_sys as ffi;
6
7#[derive(Debug)]
8pub enum ReadError {
9    OnCreateErr(&'static str),
10    OnRead {
11        code: ReadCode,
12        msg: &'static str,
13        pos: usize,
14    },
15}
16
17impl std::fmt::Display for ReadError {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        use ReadError::*;
20        match self {
21            OnCreateErr(s) => write!(f, "On create error: {}", s),
22            OnRead { code, msg, pos } => {
23                write!(f, "{:?} at pos {pos}: {}", code, msg)
24            }
25        }
26    }
27}
28
29impl std::error::Error for ReadError {}
30
31#[derive(Debug, FromPrimitive, PartialEq)]
32#[repr(u32)]
33pub enum ReadCode {
34    Success = 0,
35    ErrorInvalidParameter = 1,
36    ErrorMemoryAllocation = 2,
37    ErrorEmptyContent = 3,
38    ErrorUnexpectedContent = 4,
39    ErrorUnexpectedEnd = 5,
40    ErrorUnexpectedCharacter = 6,
41    ErrorJsonStructure = 7,
42    ErrorInvalidComment = 8,
43    ErrorInvalidNumber = 9,
44    ErrorInvalidString = 10,
45    ErrorLiteral = 11,
46    ErrorFileOpen = 12,
47    ErrorFileRead = 13,
48}
49
50impl ReadError {
51    #[inline(always)]
52    pub fn is_mem_allocation_err(&self) -> bool {
53        match self {
54            Self::OnRead { code, .. } => code == &ReadCode::ErrorMemoryAllocation,
55            _ => false,
56        }
57    }
58}
59
60impl From<&ffi::yyjson_read_err> for ReadError {
61    fn from(v: &ffi::yyjson_read_err) -> Self {
62        use ReadError::*;
63        let code: ReadCode = match num_traits::cast::FromPrimitive::from_u32(v.code) {
64            Some(v) => v,
65            None => return OnCreateErr("yyjson_read_code should be in range 0..=13"),
66        };
67
68        if code == ReadCode::Success {
69            return OnCreateErr("yyjson_read_code is SUCCESS (non-error)");
70        }
71
72        if v.msg.is_null() {
73            return OnCreateErr("error msg string is null yet error occured");
74        }
75
76        // SAFETY: if an error occured (i.e. read_code is not SUCCESS) then
77        // msg is guaranteed to be non-null and point to a statically allocated
78        // c-string if v is non error
79        let msg = {
80            let as_c_str = unsafe { CStr::from_ptr(v.msg) };
81            match as_c_str.to_str() {
82                Ok(s) => s,
83                Err(_) => return OnCreateErr("error msg string is invalid utf-8"),
84            }
85        };
86
87        OnRead {
88            code,
89            msg,
90            pos: v.pos,
91        }
92    }
93}
94
95#[derive(Debug, Default)]
96pub struct ReadOptions {
97    // stop reading when reaching the end of a JSON document instead of
98    // issuing an error if there's additional content after it. This is for
99    // reading small pieces of JSON within larger data, such as NDJSON
100    pub stop_when_done: bool,
101    pub allow_trailing_commas: bool,
102    pub allow_comments: bool,
103    pub allow_inf_and_nan: bool,
104    pub bignums_as_raw_strings: bool,
105}
106
107impl ReadOptions {
108    pub fn to_read_flag(&self) -> u32 {
109        use ffi::*;
110        let mut flag: u32 = YYJSON_READ_NOFLAG;
111        if self.stop_when_done {
112            flag |= YYJSON_READ_STOP_WHEN_DONE;
113        }
114        if self.allow_trailing_commas {
115            flag |= YYJSON_READ_ALLOW_TRAILING_COMMAS;
116        }
117        if self.allow_comments {
118            flag |= YYJSON_READ_ALLOW_COMMENTS;
119        }
120        if self.allow_inf_and_nan {
121            flag |= YYJSON_READ_ALLOW_INF_AND_NAN;
122        }
123        if self.bignums_as_raw_strings {
124            flag |= YYJSON_READ_BIGNUM_AS_RAW;
125        }
126        return flag;
127    }
128}