Skip to main content

reifydb_type/value/boolean/
parse.rs

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