reifydb_type/value/boolean/
parse.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the MIT, see license.md file
3
4use crate::{
5	Error, err,
6	error::diagnostic::boolean::{empty_boolean_value, invalid_boolean_format, invalid_number_boolean},
7	fragment::IntoFragment,
8	return_error,
9};
10
11pub fn parse_bool<'a>(fragment: impl IntoFragment<'a>) -> Result<bool, Error> {
12	let fragment = fragment.into_fragment();
13	let value = fragment.text().trim();
14
15	if value.is_empty() {
16		return_error!(empty_boolean_value(fragment));
17	}
18
19	// Fast path: byte-level matching for common cases
20	match value.as_bytes() {
21		b"true" | b"TRUE" | b"True" => return Ok(true),
22		b"false" | b"FALSE" | b"False" => return Ok(false),
23		b"1" | b"1.0" => return Ok(true),
24		b"0" | b"0.0" => return Ok(false),
25		_ => {}
26	}
27
28	// Slow path: case-insensitive matching for mixed case
29	match value.len() {
30		4 if value.eq_ignore_ascii_case("true") => Ok(true),
31		5 if value.eq_ignore_ascii_case("false") => Ok(false),
32		3 if value == "1.0" => Ok(true),
33		3 if value == "0.0" => Ok(false),
34		_ => {
35			// Check if the value contains numbers - if so, use
36			// numeric boolean diagnostic
37			if value.as_bytes().iter().any(|&b| b.is_ascii_digit()) {
38				err!(invalid_number_boolean(fragment))
39			} else {
40				err!(invalid_boolean_format(fragment))
41			}
42		}
43	}
44}
45
46#[cfg(test)]
47mod tests {
48	use super::*;
49	use crate::OwnedFragment;
50
51	#[test]
52	fn test_valid_true() {
53		assert_eq!(parse_bool(OwnedFragment::testing("true")), Ok(true));
54	}
55
56	#[test]
57	fn test_valid_false() {
58		assert_eq!(parse_bool(OwnedFragment::testing("false")), Ok(false));
59	}
60
61	#[test]
62	fn test_valid_true_with_spaces() {
63		assert_eq!(parse_bool(OwnedFragment::testing("  true  ")), Ok(true));
64	}
65
66	#[test]
67	fn test_valid_false_with_spaces() {
68		assert_eq!(parse_bool(OwnedFragment::testing("  false  ")), Ok(false));
69	}
70
71	#[test]
72	fn test_case_mismatch_true() {
73		assert_eq!(parse_bool(OwnedFragment::testing("True")), Ok(true));
74		assert_eq!(parse_bool(OwnedFragment::testing("TRUE")), Ok(true));
75		assert_eq!(parse_bool(OwnedFragment::testing("tRuE")), Ok(true));
76	}
77
78	#[test]
79	fn test_case_mismatch_false() {
80		assert_eq!(parse_bool(OwnedFragment::testing("False")), Ok(false));
81		assert_eq!(parse_bool(OwnedFragment::testing("FALSE")), Ok(false));
82		assert_eq!(parse_bool(OwnedFragment::testing("fAlSe")), Ok(false));
83	}
84
85	#[test]
86	fn test_valid_numeric_boolean() {
87		assert_eq!(parse_bool(OwnedFragment::testing("1")), Ok(true));
88		assert_eq!(parse_bool(OwnedFragment::testing("0")), Ok(false));
89		assert_eq!(parse_bool(OwnedFragment::testing("1.0")), Ok(true));
90		assert_eq!(parse_bool(OwnedFragment::testing("0.0")), Ok(false));
91	}
92
93	#[test]
94	fn test_invalid_numeric_boolean() {
95		assert!(parse_bool(OwnedFragment::testing("2")).is_err());
96		assert!(parse_bool(OwnedFragment::testing("1.5")).is_err());
97		assert!(parse_bool(OwnedFragment::testing("0.5")).is_err());
98		assert!(parse_bool(OwnedFragment::testing("-1")).is_err());
99		assert!(parse_bool(OwnedFragment::testing("100")).is_err());
100	}
101
102	#[test]
103	fn test_empty_boolean_value() {
104		assert!(parse_bool(OwnedFragment::testing("")).is_err());
105		assert!(parse_bool(OwnedFragment::testing("   ")).is_err());
106	}
107
108	#[test]
109	fn test_ambiguous_boolean_value() {
110		assert!(parse_bool(OwnedFragment::testing("yes")).is_err());
111		assert!(parse_bool(OwnedFragment::testing("no")).is_err());
112		assert!(parse_bool(OwnedFragment::testing("y")).is_err());
113		assert!(parse_bool(OwnedFragment::testing("n")).is_err());
114		assert!(parse_bool(OwnedFragment::testing("on")).is_err());
115		assert!(parse_bool(OwnedFragment::testing("off")).is_err());
116		assert!(parse_bool(OwnedFragment::testing("t")).is_err());
117		assert!(parse_bool(OwnedFragment::testing("f")).is_err());
118	}
119
120	#[test]
121	fn test_invalid_boolean_format() {
122		assert!(parse_bool(OwnedFragment::testing("invalid")).is_err());
123		assert!(parse_bool(OwnedFragment::testing("123")).is_err());
124		assert!(parse_bool(OwnedFragment::testing("abc")).is_err());
125		assert!(parse_bool(OwnedFragment::testing("maybe")).is_err());
126	}
127
128	#[test]
129	fn test_case_insensitive_ambiguous() {
130		assert!(parse_bool(OwnedFragment::testing("Yes")).is_err());
131		assert!(parse_bool(OwnedFragment::testing("NO")).is_err());
132		assert!(parse_bool(OwnedFragment::testing("On")).is_err());
133		assert!(parse_bool(OwnedFragment::testing("OFF")).is_err());
134	}
135}