nojson/raw.rs
1use std::{borrow::Cow, fmt::Display, hash::Hash, ops::Range};
2
3use crate::{
4 DisplayJson, JsonFormatter, JsonValueKind,
5 parse::{JsonParser, Jsonc, Plain},
6};
7
8pub use crate::parse_error::JsonParseError;
9
10/// Owned version of [`RawJson`].
11#[derive(Debug, Clone)]
12pub struct RawJsonOwned {
13 text: String,
14 values: Vec<JsonValueIndexEntry>,
15}
16
17impl RawJsonOwned {
18 /// Parses a JSON string into a [`RawJsonOwned`] instance.
19 ///
20 /// This validates the JSON syntax without converting values to Rust types.
21 /// Unlike [`RawJson::parse`], this creates an owned version that doesn't
22 /// borrow from the input string.
23 ///
24 /// # Example
25 ///
26 /// ```
27 /// # use nojson::RawJsonOwned;
28 /// # fn main() -> Result<(), nojson::JsonParseError> {
29 /// let text = r#"{"name": "John", "age": 30}"#;
30 /// let json = RawJsonOwned::parse(text)?;
31 /// # Ok(())
32 /// # }
33 /// ```
34 pub fn parse<T>(text: T) -> Result<Self, JsonParseError>
35 where
36 T: Into<String>,
37 {
38 let text = text.into();
39 let (values, _) = JsonParser::<Plain>::new(&text).parse()?;
40 Ok(Self { text, values })
41 }
42
43 /// Parses a JSONC (JSON with Comments) string into a [`RawJsonOwned`] instance.
44 ///
45 /// This validates the JSONC syntax and strips out comments, returning both
46 /// the parsed JSON structure and the byte ranges where comments were found
47 /// in the original text. Comments can be either line comments (`//`) or
48 /// block comments (`/* */`). Additionally, this parser allows trailing commas
49 /// in objects and arrays.
50 ///
51 /// Unlike [`RawJson::parse_jsonc`], this creates an owned version that doesn't
52 /// borrow from the input string.
53 ///
54 /// # Example
55 ///
56 /// ```
57 /// # use nojson::RawJsonOwned;
58 /// # fn main() -> Result<(), nojson::JsonParseError> {
59 /// let text = r#"{
60 /// "name": "John", // This is a comment
61 /// "age": 30,
62 /// /*
63 /// * This is a multi-line
64 /// * block comment
65 /// */
66 /// "city": "New York", // Trailing comma is allowed
67 /// }"#;
68 ///
69 /// let (json, comment_ranges) = RawJsonOwned::parse_jsonc(text)?;
70 ///
71 /// // The parsed JSON works normally
72 /// let name: String = json.value().to_member("name")?.required()?.try_into()?;
73 /// assert_eq!(name, "John");
74 ///
75 /// // Comment ranges indicate where comments were found in the original text
76 /// assert_eq!(comment_ranges.len(), 3); // Three comments found
77 ///
78 /// // You can extract the comment text if needed
79 /// let first_comment = &text[comment_ranges[0].clone()];
80 /// assert!(first_comment.contains("This is a comment"));
81 /// # Ok(())
82 /// # }
83 /// ```
84 pub fn parse_jsonc<T>(text: T) -> Result<(Self, Vec<Range<usize>>), JsonParseError>
85 where
86 T: Into<String>,
87 {
88 let text = text.into();
89 let (values, comments) = JsonParser::<Jsonc>::new(&text).parse()?;
90 Ok((Self { text, values }, comments))
91 }
92
93 /// Returns the original JSON text.
94 pub fn text(&self) -> &str {
95 &self.text
96 }
97
98 /// Returns the top-level value of the JSON.
99 ///
100 /// This value can be used as an entry point to traverse the entire JSON structure
101 /// and convert it to Rust types.
102 ///
103 /// # Example
104 ///
105 /// ```
106 /// # use nojson::RawJsonOwned;
107 /// # fn main() -> Result<(), nojson::JsonParseError> {
108 /// let text = r#"{"name": "John", "age": 30}"#;
109 /// let json = RawJsonOwned::parse(text).unwrap();
110 /// let value = json.value();
111 /// # Ok(())
112 /// # }
113 /// ```
114 pub fn value(&self) -> RawJsonValue<'_, '_> {
115 RawJsonValue {
116 json: self.as_raw_json_ref(),
117 index: 0,
118 }
119 }
120
121 /// Finds the JSON value at the specified byte position in the original text.
122 ///
123 /// This method traverses the JSON structure to find the most specific value
124 /// that contains the given position.
125 /// It returns `None` if the position is outside the bounds of the JSON text.
126 ///
127 /// This method is useful for retrieving the context
128 /// where a [`JsonParseError::InvalidValue`] error occurred.
129 ///
130 /// # Example
131 ///
132 /// ```
133 /// # use nojson::RawJsonOwned;
134 /// # fn main() -> Result<(), nojson::JsonParseError> {
135 /// let json = RawJsonOwned::parse(r#"{"name": "John", "age": 30}"#)?;
136 ///
137 /// // Position at "name" key
138 /// let name_value = json.get_value_by_position(2).expect("infallible");
139 /// assert_eq!(name_value.as_raw_str(), r#""name""#);
140 ///
141 /// // Position at number value
142 /// let age_value = json.get_value_by_position(25).expect("infallible");
143 /// assert_eq!(age_value.as_raw_str(), "30");
144 /// # Ok(())
145 /// # }
146 /// ```
147 pub fn get_value_by_position(&self, position: usize) -> Option<RawJsonValue<'_, '_>> {
148 self.as_raw_json_ref().get_value_by_position(position)
149 }
150
151 fn as_raw_json_ref(&self) -> RawJsonRef<'_, '_> {
152 RawJsonRef {
153 text: &self.text,
154 values: &self.values,
155 }
156 }
157}
158
159impl PartialEq for RawJsonOwned {
160 fn eq(&self, other: &Self) -> bool {
161 self.text == other.text
162 }
163}
164
165impl Eq for RawJsonOwned {}
166
167impl PartialOrd for RawJsonOwned {
168 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
169 Some(self.cmp(other))
170 }
171}
172
173impl Ord for RawJsonOwned {
174 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
175 self.text.cmp(&other.text)
176 }
177}
178
179impl Hash for RawJsonOwned {
180 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
181 self.text.hash(state);
182 }
183}
184
185impl Display for RawJsonOwned {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 write!(f, "{}", crate::Json(self))
188 }
189}
190
191impl DisplayJson for RawJsonOwned {
192 fn fmt(&self, f: &mut JsonFormatter<'_, '_>) -> std::fmt::Result {
193 DisplayJson::fmt(&self.value(), f)
194 }
195}
196
197impl std::str::FromStr for RawJsonOwned {
198 type Err = JsonParseError;
199
200 fn from_str(s: &str) -> Result<Self, Self::Err> {
201 Self::parse(s)
202 }
203}
204
205impl<'text, 'raw> TryFrom<RawJsonValue<'text, 'raw>> for RawJsonOwned {
206 type Error = JsonParseError;
207
208 fn try_from(value: RawJsonValue<'text, 'raw>) -> Result<Self, Self::Error> {
209 Ok(value.extract().into_owned())
210 }
211}
212
213/// Parsed JSON text (syntactically correct, but not yet converted to Rust types).
214///
215/// This struct holds a JSON text in its original form
216/// (i.e., JSON integers are not converted to Rust's integers),
217/// while ensuring the text is valid JSON syntax.
218///
219/// [`RawJson`] maintains index information about each JSON value in the text,
220/// including its type ([`JsonValueKind`]) and the start and end byte positions.
221/// You can traverse the JSON structure by accessing the top-level value
222/// via [`RawJson::value()`], which returns a [`RawJsonValue`]
223/// that provides methods to explore nested elements and convert them into Rust types.
224///
225/// Note that, for simple use cases,
226/// using [`Json`](crate::Json), which internally uses [`RawJson`], is a more convenient way to parse JSON text into Rust types.
227#[derive(Debug, Clone)]
228pub struct RawJson<'text> {
229 text: &'text str,
230 values: Vec<JsonValueIndexEntry>,
231}
232
233impl<'text> RawJson<'text> {
234 /// Parses a JSON string into a [`RawJson`] instance.
235 ///
236 /// This validates the JSON syntax without converting values to Rust types.
237 ///
238 /// # Example
239 ///
240 /// ```
241 /// # use nojson::RawJson;
242 /// # fn main() -> Result<(), nojson::JsonParseError> {
243 /// let text = r#"{"name": "John", "age": 30}"#;
244 /// let json = RawJson::parse(text)?;
245 /// # Ok(())
246 /// # }
247 /// ```
248 pub fn parse(text: &'text str) -> Result<Self, JsonParseError> {
249 let (values, _) = JsonParser::<Plain>::new(text).parse()?;
250 Ok(Self { text, values })
251 }
252
253 /// Parses a JSONC (JSON with Comments) string into a [`RawJson`] instance.
254 ///
255 /// This validates the JSONC syntax and strips out comments, returning both
256 /// the parsed JSON structure and the byte ranges where comments were found
257 /// in the original text. Comments can be either line comments (`//`) or
258 /// block comments (`/* */`). Additionally, this parser allows trailing commas
259 /// in objects and arrays.
260 ///
261 /// # Example
262 ///
263 /// ```
264 /// # use nojson::RawJson;
265 /// # fn main() -> Result<(), nojson::JsonParseError> {
266 /// let text = r#"{
267 /// "name": "John", // This is a comment
268 /// "age": 30,
269 /// /* This is a block comment */
270 /// "city": "New York", // Trailing comma is allowed
271 /// }"#;
272 ///
273 /// let (json, comment_ranges) = RawJson::parse_jsonc(text)?;
274 ///
275 /// // The parsed JSON works normally
276 /// let name: String = json.value().to_member("name")?.required()?.try_into()?;
277 /// assert_eq!(name, "John");
278 ///
279 /// // Comment ranges indicate where comments were found in the original text
280 /// assert_eq!(comment_ranges.len(), 3); // Three comments found
281 ///
282 /// // You can extract the comment text if needed
283 /// let first_comment = &text[comment_ranges[0].clone()];
284 /// assert!(first_comment.contains("This is a comment"));
285 /// # Ok(())
286 /// # }
287 /// ```
288 pub fn parse_jsonc(text: &'text str) -> Result<(Self, Vec<Range<usize>>), JsonParseError> {
289 let (values, comments) = JsonParser::<Jsonc>::new(text).parse()?;
290 Ok((Self { text, values }, comments))
291 }
292
293 /// Returns the original JSON text.
294 pub fn text(&self) -> &'text str {
295 self.text
296 }
297
298 /// Returns the top-level value of the JSON.
299 ///
300 /// This value can be used as an entry point to traverse the entire JSON structure
301 /// and convert it to Rust types.
302 ///
303 /// # Example
304 ///
305 /// ```
306 /// # use nojson::RawJson;
307 /// # fn main() -> Result<(), nojson::JsonParseError> {
308 /// let text = r#"{"name": "John", "age": 30}"#;
309 /// let json = RawJson::parse(text).unwrap();
310 /// let value = json.value();
311 /// # Ok(())
312 /// # }
313 /// ```
314 pub fn value(&self) -> RawJsonValue<'text, '_> {
315 RawJsonValue {
316 json: self.as_raw_json_ref(),
317 index: 0,
318 }
319 }
320
321 /// Finds the JSON value at the specified byte position in the original text.
322 ///
323 /// This method traverses the JSON structure to find the most specific value
324 /// that contains the given position.
325 /// It returns `None` if the position is outside the bounds of the JSON text.
326 ///
327 /// This method is useful for retrieving the context
328 /// where a [`JsonParseError::InvalidValue`] error occurred.
329 ///
330 /// # Example
331 ///
332 /// ```
333 /// # use nojson::RawJson;
334 /// # fn main() -> Result<(), nojson::JsonParseError> {
335 /// let json = RawJson::parse(r#"{"name": "John", "age": 30}"#)?;
336 ///
337 /// // Position at "name" key
338 /// let name_value = json.get_value_by_position(2).expect("infallible");
339 /// assert_eq!(name_value.as_raw_str(), r#""name""#);
340 ///
341 /// // Position at number value
342 /// let age_value = json.get_value_by_position(25).expect("infallible");
343 /// assert_eq!(age_value.as_raw_str(), "30");
344 /// # Ok(())
345 /// # }
346 /// ```
347 pub fn get_value_by_position(&self, position: usize) -> Option<RawJsonValue<'text, '_>> {
348 self.as_raw_json_ref().get_value_by_position(position)
349 }
350
351 /// Converts this borrowed [`RawJson`] into an owned [`RawJsonOwned`].
352 ///
353 /// This method creates an owned copy of the JSON data, allowing it to be used
354 /// beyond the lifetime of the original text. The resulting [`RawJsonOwned`]
355 /// contains its own copy of the JSON text and can be moved freely.
356 ///
357 /// # Example
358 ///
359 /// ```
360 /// # use nojson::RawJson;
361 /// # fn main() -> Result<(), nojson::JsonParseError> {
362 /// let text = r#"{"name": "John", "age": 30}"#;
363 /// let json = RawJson::parse(text)?;
364 /// let owned_json = json.into_owned();
365 ///
366 /// // The owned version can outlive the original text
367 /// drop(text);
368 /// assert_eq!(owned_json.text(), r#"{"name": "John", "age": 30}"#);
369 /// # Ok(())
370 /// # }
371 /// ```
372 pub fn into_owned(self) -> RawJsonOwned {
373 RawJsonOwned {
374 text: self.text.to_owned(),
375 values: self.values,
376 }
377 }
378
379 fn as_raw_json_ref(&self) -> RawJsonRef<'text, '_> {
380 RawJsonRef {
381 text: self.text,
382 values: &self.values,
383 }
384 }
385}
386
387impl PartialEq for RawJson<'_> {
388 fn eq(&self, other: &Self) -> bool {
389 self.text == other.text
390 }
391}
392
393impl Eq for RawJson<'_> {}
394
395impl PartialOrd for RawJson<'_> {
396 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
397 Some(self.cmp(other))
398 }
399}
400
401impl Ord for RawJson<'_> {
402 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
403 self.text.cmp(other.text)
404 }
405}
406
407impl Hash for RawJson<'_> {
408 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
409 self.text.hash(state);
410 }
411}
412
413impl Display for RawJson<'_> {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 write!(f, "{}", crate::Json(self))
416 }
417}
418
419impl DisplayJson for RawJson<'_> {
420 fn fmt(&self, f: &mut JsonFormatter<'_, '_>) -> std::fmt::Result {
421 DisplayJson::fmt(&self.value(), f)
422 }
423}
424
425impl<'text, 'raw> TryFrom<RawJsonValue<'text, 'raw>> for RawJson<'text> {
426 type Error = JsonParseError;
427
428 fn try_from(value: RawJsonValue<'text, 'raw>) -> Result<Self, Self::Error> {
429 Ok(value.extract())
430 }
431}
432
433#[derive(Debug, Clone, PartialEq, Eq)]
434pub(crate) struct JsonValueIndexEntry {
435 pub kind: JsonValueKind,
436 pub escaped: bool,
437 pub text: Range<usize>,
438 pub end_index: usize,
439}
440
441#[derive(Debug, Clone, Copy)]
442struct RawJsonRef<'text, 'raw> {
443 text: &'text str,
444 values: &'raw [JsonValueIndexEntry],
445}
446
447impl<'text, 'raw> RawJsonRef<'text, 'raw> {
448 fn get_value_by_position(self, position: usize) -> Option<RawJsonValue<'text, 'raw>> {
449 let mut value = self.value();
450 if !value.entry().text.contains(&position) {
451 return None;
452 }
453 while let Some(child) = Children::new(value).find(|c| c.entry().text.contains(&position)) {
454 value = child;
455 }
456 Some(value)
457 }
458
459 fn value(self) -> RawJsonValue<'text, 'raw> {
460 RawJsonValue {
461 json: self,
462 index: 0,
463 }
464 }
465}
466
467impl PartialEq for RawJsonRef<'_, '_> {
468 fn eq(&self, other: &Self) -> bool {
469 self.text == other.text
470 }
471}
472
473impl Eq for RawJsonRef<'_, '_> {}
474
475impl PartialOrd for RawJsonRef<'_, '_> {
476 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
477 Some(self.cmp(other))
478 }
479}
480
481impl Ord for RawJsonRef<'_, '_> {
482 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
483 self.text.cmp(other.text)
484 }
485}
486
487impl Hash for RawJsonRef<'_, '_> {
488 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
489 self.text.hash(state);
490 }
491}
492
493impl Display for RawJsonRef<'_, '_> {
494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
495 write!(f, "{}", crate::Json(self))
496 }
497}
498
499impl DisplayJson for RawJsonRef<'_, '_> {
500 fn fmt(&self, f: &mut JsonFormatter<'_, '_>) -> std::fmt::Result {
501 DisplayJson::fmt(&self.value(), f)
502 }
503}
504
505/// A JSON value in a [`RawJson`].
506///
507/// This struct provides the text and structural information (e.g., kind, parent, children) of a JSON value.
508/// Interpreting that text is the responsibility of the user.
509///
510/// To convert this JSON value to a Rust type, you can use the standard [`TryFrom`] and [`TryInto`] traits.
511/// For other parsing approaches, you can use the [`FromStr`](std::str::FromStr) trait or other parsing methods
512/// to parse the underlying JSON text of this value as shown below:
513///
514/// ```
515/// # use nojson::{RawJson, RawJsonValue, JsonParseError};
516/// # fn main() -> Result<(), JsonParseError> {
517/// let text = "1.23";
518/// let json = RawJson::parse(text)?;
519/// let raw: RawJsonValue = json.value();
520/// let parsed: f32 =
521/// raw.as_number_str()?.parse().map_err(|e| raw.invalid(e))?;
522/// assert_eq!(parsed, 1.23);
523/// # Ok(())
524/// # }
525/// ```
526///
527/// For types that implement `TryFrom<RawJsonValue<'_, '_>>`, you can use the [`TryInto`] trait:
528///
529/// ```
530/// # use nojson::{RawJson, JsonParseError};
531/// # fn main() -> Result<(), JsonParseError> {
532/// let json = RawJson::parse("[1, 2, 3]")?;
533/// let numbers: [u32; 3] = json.value().try_into()?;
534/// assert_eq!(numbers, [1, 2, 3]);
535/// # Ok(())
536/// # }
537/// ```
538#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
539pub struct RawJsonValue<'text, 'raw> {
540 index: usize,
541 json: RawJsonRef<'text, 'raw>,
542}
543
544impl<'text, 'raw> RawJsonValue<'text, 'raw> {
545 /// Returns the kind of this JSON value.
546 pub fn kind(self) -> JsonValueKind {
547 self.json.values[self.index].kind
548 }
549
550 /// Returns the byte position where this value begins in the JSON text (`self.json().text()`).
551 pub fn position(self) -> usize {
552 self.json.values[self.index].text.start
553 }
554
555 /// Returns the parent value (array or object) that contains this value.
556 pub fn parent(self) -> Option<Self> {
557 if self.index == 0 {
558 return None;
559 }
560 self.json.get_value_by_position(self.position() - 1)
561 }
562
563 /// Returns the root (top-level) value of the JSON data.
564 ///
565 /// This method navigates back to the root value of the entire JSON structure,
566 /// regardless of where this value is located in the JSON hierarchy. This is
567 /// useful when you want to access other parts of the JSON from a nested value.
568 ///
569 /// This operation is O(1) and more efficient than repeatedly calling [`parent()`](Self::parent)
570 /// to reach the root.
571 ///
572 /// # Examples
573 ///
574 /// ```
575 /// # fn main() -> Result<(), nojson::JsonParseError> {
576 /// let json = nojson::RawJson::parse(r#"{"user": {"name": "John", "age": 30}, "count": 42}"#)?;
577 /// let age_value = json.value()
578 /// .to_member("user")?
579 /// .required()?
580 /// .to_member("age")?
581 /// .required()?;
582 ///
583 /// // From the nested age value, navigate back to the root
584 /// let root = age_value.root();
585 ///
586 /// // Access any part of the original JSON structure
587 /// let count: i32 = root.to_member("count")?.required()?.try_into()?;
588 /// assert_eq!(count, 42);
589 ///
590 /// let user_name: String = root
591 /// .to_member("user")?
592 /// .required()?
593 /// .to_member("name")?
594 /// .required()?
595 /// .try_into()?;
596 /// assert_eq!(user_name, "John");
597 /// # Ok(())
598 /// # }
599 /// ```
600 pub fn root(self) -> Self {
601 Self {
602 json: self.json,
603 index: 0,
604 }
605 }
606
607 /// Returns the raw JSON text of this value as-is.
608 pub fn as_raw_str(self) -> &'text str {
609 let text = &self.json.values[self.index].text;
610 &self.json.text[text.start..text.end]
611 }
612
613 /// Converts this value to a borrowed [`RawJson`] containing just this value and its children.
614 ///
615 /// This method creates a borrowed view of this specific JSON value and its text,
616 /// including all nested children if the value is an object or array. The resulting
617 /// [`RawJson`] contains only this value and its descendants as its root,
618 /// not the entire original JSON text.
619 ///
620 /// If you need an owned version, you can call `.into_owned()` on the result.
621 ///
622 /// # Example
623 ///
624 /// ```
625 /// # use nojson::RawJson;
626 /// # fn main() -> Result<(), nojson::JsonParseError> {
627 /// let text = r#"{"user": {"name": "John", "age": 30}, "count": 42}"#;
628 /// let json = RawJson::parse(text)?;
629 /// let user_value = json.value().to_member("user")?.required()?;
630 ///
631 /// // Extract the user object and its children to borrowed
632 /// let borrowed_user = user_value.extract();
633 ///
634 /// // The borrowed version references the original text
635 /// assert_eq!(borrowed_user.text(), r#"{"name": "John", "age": 30}"#);
636 /// let name: String = borrowed_user.value().to_member("name")?.required()?.try_into()?;
637 /// assert_eq!(name, "John");
638 ///
639 /// // Convert to owned if needed
640 /// let owned_user = borrowed_user.into_owned();
641 /// # Ok(())
642 /// # }
643 /// ```
644 pub fn extract(self) -> RawJson<'text> {
645 let start_index = self.index;
646 let end_index = self.entry().end_index;
647
648 // Extract the text range that covers this value and all its children
649 let start_pos = self.entry().text.start;
650 let end_pos = self.entry().text.end;
651 let value_text = &self.json.text[start_pos..end_pos];
652
653 // Extract all relevant value entries (this value and its children)
654 let relevant_entries = &self.json.values[start_index..end_index];
655
656 // Create new values vector with adjusted positions
657 let new_values = relevant_entries
658 .iter()
659 .map(|entry| JsonValueIndexEntry {
660 kind: entry.kind,
661 escaped: entry.escaped,
662 text: (entry.text.start - start_pos)..(entry.text.end - start_pos),
663 end_index: entry.end_index - start_index,
664 })
665 .collect();
666
667 RawJson {
668 text: value_text,
669 values: new_values,
670 }
671 }
672
673 /// Similar to [`RawJsonValue::as_raw_str()`],
674 /// but this method verifies whether the value is a JSON boolean.
675 ///
676 /// # Examples
677 ///
678 /// ```
679 /// # use nojson::RawJson;
680 /// # fn main() -> Result<(), nojson::JsonParseError> {
681 /// let json = RawJson::parse("false")?;
682 /// assert_eq!(json.value().as_boolean_str()?.parse(), Ok(false));
683 ///
684 /// let json = RawJson::parse("10")?;
685 /// assert!(json.value().as_boolean_str().is_err());
686 /// # Ok(())
687 /// # }
688 /// ```
689 pub fn as_boolean_str(self) -> Result<&'text str, JsonParseError> {
690 self.expect([JsonValueKind::Boolean])
691 .map(|v| v.as_raw_str())
692 }
693
694 /// Similar to [`RawJsonValue::as_raw_str()`],
695 /// but this method verifies whether the value is a JSON integer number.
696 ///
697 /// # Examples
698 ///
699 /// ```
700 /// # use nojson::RawJson;
701 /// # fn main() -> Result<(), nojson::JsonParseError> {
702 /// let json = RawJson::parse("123")?;
703 /// assert_eq!(json.value().as_integer_str()?.parse(), Ok(123));
704 ///
705 /// let json = RawJson::parse("12.3")?;
706 /// assert!(json.value().as_integer_str().is_err());
707 /// # Ok(())
708 /// # }
709 /// ```
710 pub fn as_integer_str(self) -> Result<&'text str, JsonParseError> {
711 self.expect([JsonValueKind::Integer])
712 .map(|v| v.as_raw_str())
713 }
714
715 /// Similar to [`RawJsonValue::as_raw_str()`],
716 /// but this method verifies whether the value is a JSON floating-point number.
717 ///
718 /// # Examples
719 ///
720 /// ```
721 /// # use nojson::RawJson;
722 /// # fn main() -> Result<(), nojson::JsonParseError> {
723 /// let json = RawJson::parse("12.3")?;
724 /// assert_eq!(json.value().as_float_str()?.parse(), Ok(12.3));
725 ///
726 /// let json = RawJson::parse("123")?;
727 /// assert!(json.value().as_float_str().is_err());
728 /// # Ok(())
729 /// # }
730 /// ```
731 pub fn as_float_str(self) -> Result<&'text str, JsonParseError> {
732 self.expect([JsonValueKind::Float]).map(|v| v.as_raw_str())
733 }
734
735 /// Similar to [`RawJsonValue::as_raw_str()`],
736 /// but this method verifies whether the value is a JSON number.
737 ///
738 /// # Examples
739 ///
740 /// ```
741 /// # use nojson::RawJson;
742 /// # fn main() -> Result<(), nojson::JsonParseError> {
743 /// let json = RawJson::parse("123")?;
744 /// assert_eq!(json.value().as_number_str()?.parse(), Ok(123));
745 ///
746 /// let json = RawJson::parse("12.3")?;
747 /// assert_eq!(json.value().as_number_str()?.parse(), Ok(12.3));
748 ///
749 /// let json = RawJson::parse("null")?;
750 /// assert!(json.value().as_number_str().is_err());
751 /// # Ok(())
752 /// # }
753 /// ```
754 pub fn as_number_str(self) -> Result<&'text str, JsonParseError> {
755 self.expect([JsonValueKind::Integer, JsonValueKind::Float])
756 .map(|v| v.as_raw_str())
757 }
758
759 /// Similar to [`RawJsonValue::as_raw_str()`],
760 /// but this method verifies whether the value is a JSON string and returns the unquoted content of the string.
761 ///
762 /// # Examples
763 ///
764 /// ```
765 /// # use nojson::RawJson;
766 /// # fn main() -> Result<(), nojson::JsonParseError> {
767 /// let json = RawJson::parse("\"123\"")?;
768 /// assert_eq!(json.value().to_unquoted_string_str()?, "123");
769 /// assert_eq!(json.value().to_unquoted_string_str()?.parse(), Ok(123));
770 ///
771 /// let json = RawJson::parse("123")?;
772 /// assert!(json.value().to_unquoted_string_str().is_err());
773 /// # Ok(())
774 /// # }
775 /// ```
776 pub fn to_unquoted_string_str(self) -> Result<Cow<'text, str>, JsonParseError> {
777 self.expect([JsonValueKind::String]).map(|v| v.unquote())
778 }
779
780 /// If the value is a JSON array,
781 /// this method returns an iterator that iterates over the array's elements.
782 ///
783 /// # Examples
784 ///
785 /// ```
786 /// # use nojson::RawJson;
787 /// # fn main() -> Result<(), nojson::JsonParseError> {
788 /// let json = RawJson::parse("[0, 1, 2]")?;
789 /// for (i, v) in json.value().to_array()?.enumerate() {
790 /// assert_eq!(v.as_integer_str()?.parse(), Ok(i));
791 /// }
792 ///
793 /// let json = RawJson::parse("null")?;
794 /// assert!(json.value().to_array().is_err());
795 /// # Ok(())
796 /// # }
797 /// ```
798 ///
799 /// # Note
800 ///
801 /// For converting to a fixed-size array, you can use the `TryInto` trait instead:
802 /// ```
803 /// # use nojson::RawJson;
804 /// # fn main() -> Result<(), nojson::JsonParseError> {
805 /// let json = RawJson::parse("[0, 1, 2]")?;
806 /// let fixed_array: [usize; 3] = json.value().try_into()?;
807 /// # Ok(())
808 /// # }
809 /// ```
810 pub fn to_array(self) -> Result<impl Iterator<Item = Self>, JsonParseError> {
811 self.expect([JsonValueKind::Array]).map(Children::new)
812 }
813
814 /// If the value is a JSON object,
815 /// this method returns an iterator that iterates over
816 /// the name and value pairs of the object's members.
817 ///
818 /// # Examples
819 ///
820 /// ```
821 /// # use nojson::RawJson;
822 /// # fn main() -> Result<(), nojson::JsonParseError> {
823 /// let json = RawJson::parse(r#"{"a": 1, "b": 2, "c": 3}"#)?;
824 /// let mut members = json.value().to_object()?;
825 /// let (k, v) = members.next().expect("some");
826 /// assert_eq!(k.to_unquoted_string_str()?, "a");
827 /// assert_eq!(v.as_integer_str()?.parse(), Ok(1));
828 ///
829 /// let json = RawJson::parse("null")?;
830 /// assert!(json.value().to_object().is_err());
831 /// # Ok(())
832 /// # }
833 /// ```
834 pub fn to_object(self) -> Result<impl Iterator<Item = (Self, Self)>, JsonParseError> {
835 self.expect([JsonValueKind::Object])
836 .map(JsonKeyValuePairs::new)
837 }
838
839 /// Attempts to access a member of a JSON object by name.
840 ///
841 /// This method returns a [`RawJsonMember`] that represents the result of
842 /// looking up the specified member name. The member may or may not exist,
843 /// and you can use methods like [`RawJsonMember::required()`] or convert
844 /// it to an `Option<T>` to handle both cases.
845 ///
846 /// # Examples
847 ///
848 /// ```
849 /// # use nojson::RawJson;
850 /// # fn main() -> Result<(), nojson::JsonParseError> {
851 /// let json = RawJson::parse(r#"{"name": "Alice", "age": 30}"#)?;
852 /// let obj = json.value();
853 ///
854 /// // Access existing member
855 /// let name_value: String = obj.to_member("name")?.required()?.try_into()?;
856 /// assert_eq!(name_value, "Alice");
857 ///
858 /// // Handle optional member
859 /// let city_member = obj.to_member("city")?;
860 /// let city: Option<String> = city_member.try_into()?;
861 /// assert_eq!(city, None);
862 /// # Ok(())
863 /// # }
864 /// ```
865 ///
866 /// # Performance
867 ///
868 /// This method has O(n) complexity where n is the number of members in the object,
869 /// as it performs a linear search through all object members to find the requested name.
870 /// If you need to access multiple members from the same object, consider using
871 /// [`RawJsonValue::to_object()`] instead, which allows you to iterate through
872 /// all members once and extract the values you need more efficiently.
873 ///
874 /// ```
875 /// # use nojson::RawJson;
876 /// # fn main() -> Result<(), nojson::JsonParseError> {
877 /// let json = RawJson::parse(r#"{"name": "Alice", "age": 30, "city": "New York"}"#)?;
878 /// let obj = json.value();
879 ///
880 /// // Efficient: single iteration for multiple members
881 /// let mut name = None;
882 /// let mut age = None;
883 /// let mut city = None;
884 /// for (key, value) in obj.to_object()? {
885 /// match key.to_unquoted_string_str()?.as_ref() {
886 /// "name" => name = Some(value),
887 /// "age" => age = Some(value),
888 /// "city" => city = Some(value),
889 /// _ => {}
890 /// }
891 /// }
892 /// # Ok(())
893 /// # }
894 /// ```
895 pub fn to_member<'a>(
896 self,
897 name: &'a str,
898 ) -> Result<RawJsonMember<'text, 'raw, 'a>, JsonParseError> {
899 let member = self
900 .to_object()?
901 .find(|(key, _)| key.unquote() == name)
902 .map(|(_, value)| value);
903
904 Ok(RawJsonMember {
905 object: self,
906 name,
907 member,
908 })
909 }
910
911 /// Applies a transformation function to this JSON value.
912 ///
913 /// This method allows you to transform a `RawJsonValue` into any other type `T`
914 /// using a closure that can potentially fail with a `JsonParseError`. It's particularly
915 /// useful for chaining operations or applying custom parsing logic.
916 ///
917 /// # Examples
918 ///
919 /// ```
920 /// # use nojson::RawJson;
921 /// # fn main() -> Result<(), nojson::JsonParseError> {
922 /// let json = RawJson::parse("\"42\"")?;
923 ///
924 /// // Transform a string value to an integer
925 /// let number: i32 = json.value().map(|v| {
926 /// v.to_unquoted_string_str()?.parse().map_err(|e| v.invalid(e))
927 /// })?;
928 /// assert_eq!(number, 42);
929 /// # Ok(())
930 /// # }
931 /// ```
932 ///
933 /// This method is equivalent to directly calling the function with the value,
934 /// but provides a more functional programming style for chaining operations.
935 pub fn map<F, T>(self, f: F) -> Result<T, JsonParseError>
936 where
937 F: FnOnce(RawJsonValue<'text, 'raw>) -> Result<T, JsonParseError>,
938 {
939 f(self)
940 }
941
942 /// Creates a [`JsonParseError::InvalidValue`] error for this value.
943 ///
944 /// This is a convenience method that's equivalent to calling
945 /// [`JsonParseError::invalid_value()`] with this value.
946 ///
947 /// # Examples
948 ///
949 /// ```
950 /// # use nojson::RawJson;
951 /// # fn main() -> Result<(), nojson::JsonParseError> {
952 /// let json = RawJson::parse("\"not_a_number\"")?;
953 /// let value = json.value();
954 ///
955 /// // These are equivalent:
956 /// let error1 = value.invalid("expected a number");
957 /// let error2 = nojson::JsonParseError::invalid_value(value, "expected a number");
958 /// # Ok(())
959 /// # }
960 /// ```
961 pub fn invalid<E>(self, error: E) -> JsonParseError
962 where
963 E: Into<Box<dyn Send + Sync + std::error::Error>>,
964 {
965 JsonParseError::invalid_value(self, error)
966 }
967
968 fn unquote(self) -> Cow<'text, str> {
969 debug_assert!(self.kind().is_string());
970
971 let content = &self.as_raw_str()[1..self.as_raw_str().len() - 1];
972 if !self.entry().escaped {
973 return Cow::Borrowed(content);
974 }
975
976 let mut unescaped = String::with_capacity(content.len());
977 let mut chars = content.chars();
978 while let Some(c) = chars.next() {
979 match c {
980 '\\' => {
981 let c = chars.next().expect("infallible");
982 match c {
983 '\\' | '/' | '"' => unescaped.push(c),
984 'n' => unescaped.push('\n'),
985 't' => unescaped.push('\t'),
986 'r' => unescaped.push('\r'),
987 'b' => unescaped.push('\u{8}'),
988 'f' => unescaped.push('\u{c}'),
989 'u' => {
990 let c = std::str::from_utf8(&[
991 chars.next().expect("infallible") as u8,
992 chars.next().expect("infallible") as u8,
993 chars.next().expect("infallible") as u8,
994 chars.next().expect("infallible") as u8,
995 ])
996 .ok()
997 .and_then(|code| u32::from_str_radix(code, 16).ok())
998 .and_then(char::from_u32)
999 .expect("infallible");
1000 unescaped.push(c);
1001 }
1002 _ => unreachable!(),
1003 }
1004 }
1005 _ => unescaped.push(c),
1006 }
1007 }
1008 Cow::Owned(unescaped)
1009 }
1010
1011 fn expect<const N: usize>(self, kinds: [JsonValueKind; N]) -> Result<Self, JsonParseError> {
1012 if kinds.contains(&self.kind()) {
1013 Ok(self)
1014 } else {
1015 Err(self.invalid(format!(
1016 "expected {}, but found {:?}",
1017 if kinds.len() == 1 {
1018 format!("{:?}", kinds[0])
1019 } else {
1020 format!("one of {kinds:?}")
1021 },
1022 self.kind()
1023 )))
1024 }
1025 }
1026
1027 fn entry(&self) -> &JsonValueIndexEntry {
1028 &self.json.values[self.index]
1029 }
1030}
1031
1032impl Display for RawJsonValue<'_, '_> {
1033 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1034 write!(f, "{}", crate::Json(self))
1035 }
1036}
1037
1038impl DisplayJson for RawJsonValue<'_, '_> {
1039 fn fmt(&self, f: &mut JsonFormatter<'_, '_>) -> std::fmt::Result {
1040 match self.kind() {
1041 JsonValueKind::Null
1042 | JsonValueKind::Boolean
1043 | JsonValueKind::Integer
1044 | JsonValueKind::Float => write!(f.inner_mut(), "{}", self.as_raw_str()),
1045 JsonValueKind::String => f.string(self.unquote()),
1046 JsonValueKind::Array => f.array(|f| f.elements(self.to_array().expect("infallible"))),
1047 JsonValueKind::Object => f.object(|f| {
1048 f.members(
1049 self.to_object()
1050 .expect("infallible")
1051 .map(|(k, v)| (k.unquote(), v)),
1052 )
1053 }),
1054 }
1055 }
1056}
1057
1058#[derive(Debug)]
1059struct Children<'text, 'raw> {
1060 value: RawJsonValue<'text, 'raw>,
1061 end_index: usize,
1062}
1063
1064impl<'text, 'raw> Children<'text, 'raw> {
1065 fn new(mut value: RawJsonValue<'text, 'raw>) -> Self {
1066 let end_index = value.entry().end_index;
1067 value.index += 1;
1068 Self { value, end_index }
1069 }
1070}
1071
1072impl<'text, 'raw> Iterator for Children<'text, 'raw> {
1073 type Item = RawJsonValue<'text, 'raw>;
1074
1075 fn next(&mut self) -> Option<Self::Item> {
1076 if self.value.index == self.end_index {
1077 return None;
1078 }
1079 let value = self.value;
1080 self.value.index = value.entry().end_index;
1081 Some(value)
1082 }
1083}
1084
1085#[derive(Debug)]
1086struct JsonKeyValuePairs<'text, 'raw> {
1087 inner: Children<'text, 'raw>,
1088}
1089
1090impl<'text, 'raw> JsonKeyValuePairs<'text, 'raw> {
1091 fn new(object: RawJsonValue<'text, 'raw>) -> Self {
1092 Self {
1093 inner: Children::new(object),
1094 }
1095 }
1096}
1097
1098impl<'text, 'raw> Iterator for JsonKeyValuePairs<'text, 'raw> {
1099 type Item = (RawJsonValue<'text, 'raw>, RawJsonValue<'text, 'raw>);
1100
1101 fn next(&mut self) -> Option<Self::Item> {
1102 let key = self.inner.next()?;
1103 let value = self.inner.next().expect("infallible");
1104 Some((key, value))
1105 }
1106}
1107
1108/// Represents a member access result for a JSON object.
1109///
1110/// This struct is returned by [`RawJsonValue::to_member()`] and allows you to handle
1111/// both present and missing object members. It wraps an optional value that is
1112/// `Some` if the member exists and `None` if it doesn't.
1113///
1114/// # Examples
1115///
1116/// ```
1117/// # use nojson::RawJson;
1118/// # fn main() -> Result<(), nojson::JsonParseError> {
1119/// let json = RawJson::parse(r#"{"name": "Alice", "age": 30}"#)?;
1120/// let obj = json.value();
1121///
1122/// // Access an existing member
1123/// let name_member = obj.to_member("name")?;
1124/// let name: String = name_member.required()?.try_into()?;
1125/// assert_eq!(name, "Alice");
1126///
1127/// // Access a missing member
1128/// let city_member = obj.to_member("city")?;
1129/// let city: Option<String> = city_member.try_into()?;
1130/// assert_eq!(city, None);
1131/// # Ok(())
1132/// # }
1133/// ```
1134#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1135pub struct RawJsonMember<'text, 'raw, 'a> {
1136 object: RawJsonValue<'text, 'raw>,
1137 name: &'a str,
1138 member: Option<RawJsonValue<'text, 'raw>>,
1139}
1140
1141impl<'text, 'raw, 'a> RawJsonMember<'text, 'raw, 'a> {
1142 /// Returns the member value if it exists, or an error if it's missing.
1143 ///
1144 /// This method is useful when you need to ensure that a required member
1145 /// is present in the JSON object.
1146 ///
1147 /// # Examples
1148 ///
1149 /// ```
1150 /// # use nojson::RawJson;
1151 /// # fn main() -> Result<(), nojson::JsonParseError> {
1152 /// let json = RawJson::parse(r#"{"name": "Alice"}"#)?;
1153 /// let obj = json.value();
1154 ///
1155 /// // Required member exists
1156 /// let name = obj.to_member("name")?.required()?;
1157 /// assert_eq!(name.to_unquoted_string_str()?, "Alice");
1158 ///
1159 /// // Required member missing - returns error
1160 /// let age_result = obj.to_member("age")?.required();
1161 /// assert!(age_result.is_err());
1162 /// # Ok(())
1163 /// # }
1164 /// ```
1165 pub fn required(self) -> Result<RawJsonValue<'text, 'raw>, JsonParseError> {
1166 self.member.ok_or_else(|| {
1167 self.object
1168 .invalid(format!("required member '{}' is missing", self.name))
1169 })
1170 }
1171
1172 /// Returns the inner raw JSON value as an `Option`.
1173 ///
1174 /// This method provides direct access to the underlying `Option<RawJsonValue>`,
1175 /// allowing you to handle the presence or absence of the member yourself.
1176 ///
1177 /// # Examples
1178 ///
1179 /// ```
1180 /// # use nojson::RawJson;
1181 /// # fn main() -> Result<(), nojson::JsonParseError> {
1182 /// let json = RawJson::parse(r#"{"name": "Alice", "age": 30}"#)?;
1183 /// let obj = json.value();
1184 ///
1185 /// // Existing member
1186 /// let name_member = obj.to_member("name")?;
1187 /// if let Some(name_value) = name_member.get() {
1188 /// assert_eq!(name_value.to_unquoted_string_str()?, "Alice");
1189 /// }
1190 ///
1191 /// // Missing member
1192 /// let city_member = obj.to_member("city")?;
1193 /// assert!(city_member.get().is_none());
1194 ///
1195 /// // Using with pattern matching
1196 /// match obj.to_member("age")?.get() {
1197 /// Some(age_value) => {
1198 /// let age: i32 = age_value.as_integer_str()?.parse()
1199 /// .map_err(|e| age_value.invalid(e))?;
1200 /// assert_eq!(age, 30);
1201 /// }
1202 /// None => println!("Age not provided"),
1203 /// }
1204 /// # Ok(())
1205 /// # }
1206 /// ```
1207 pub fn get(self) -> Option<RawJsonValue<'text, 'raw>> {
1208 self.member
1209 }
1210
1211 /// Applies a transformation function to the member value if it exists.
1212 ///
1213 /// This method is similar to [`Option::map`], but designed for transformations
1214 /// that can fail with a [`JsonParseError`]. If the member exists, the function
1215 /// is applied to its value. If the member doesn't exist, `Ok(None)` is returned.
1216 ///
1217 /// # Examples
1218 ///
1219 /// ```
1220 /// # use nojson::RawJson;
1221 /// # fn main() -> Result<(), nojson::JsonParseError> {
1222 /// let json = RawJson::parse(r#"{"name": "Alice", "age": "30"}"#)?;
1223 /// let obj = json.value();
1224 ///
1225 /// // Transform existing member
1226 /// let age_member = obj.to_member("age")?;
1227 /// let age: Option<i32> = age_member.map(|v| {
1228 /// v.to_unquoted_string_str()?.parse().map_err(|e| v.invalid(e))
1229 /// })?;
1230 /// assert_eq!(age, Some(30));
1231 ///
1232 /// // Transform missing member
1233 /// let city_member = obj.to_member("city")?;
1234 /// let city: Option<String> = city_member.map(|v| v.try_into())?;
1235 /// assert_eq!(city, None);
1236 /// # Ok(())
1237 /// # }
1238 /// ```
1239 ///
1240 /// This is particularly useful when you need to perform parsing or validation
1241 /// on optional members without having to handle the `Option` separately:
1242 ///
1243 /// ```
1244 /// # use nojson::RawJson;
1245 /// # fn main() -> Result<(), nojson::JsonParseError> {
1246 /// let json = RawJson::parse(r#"{"score": "95.5"}"#)?;
1247 /// let obj = json.value();
1248 ///
1249 /// // Parse optional numeric string
1250 /// let score: Option<f64> = obj.to_member("score")?.map(|v| {
1251 /// v.to_unquoted_string_str()?.parse().map_err(|e| v.invalid(e))
1252 /// })?;
1253 /// assert_eq!(score, Some(95.5));
1254 /// # Ok(())
1255 /// # }
1256 /// ```
1257 pub fn map<F, T>(self, f: F) -> Result<Option<T>, JsonParseError>
1258 where
1259 F: FnOnce(RawJsonValue<'text, 'raw>) -> Result<T, JsonParseError>,
1260 {
1261 self.member.map(f).transpose()
1262 }
1263}
1264
1265impl<'text, 'raw, 'a, T> TryFrom<RawJsonMember<'text, 'raw, 'a>> for Option<T>
1266where
1267 T: TryFrom<RawJsonValue<'text, 'raw>, Error = JsonParseError>,
1268{
1269 type Error = JsonParseError;
1270
1271 fn try_from(value: RawJsonMember<'text, 'raw, 'a>) -> Result<Self, Self::Error> {
1272 value.member.map(T::try_from).transpose()
1273 }
1274}