#[derive(
Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum NullTokenSet {
CommonMarkers,
}
pub const NULL_MARKERS: &[&str] = &["NA", "N/A", "n/a", "#N/A", "\\N", "None"];
pub fn is_null_like_token(value: &str) -> bool {
let trimmed = value.trim();
trimmed.is_empty()
|| trimmed.eq_ignore_ascii_case("null")
|| trimmed.eq_ignore_ascii_case("nan")
|| NULL_MARKERS.contains(&trimmed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_marker_is_null_with_surrounding_whitespace() {
for marker in NULL_MARKERS {
assert!(is_null_like_token(marker), "{marker:?}");
assert!(is_null_like_token(&format!(" {marker}\t")), "{marker:?}");
}
}
#[test]
fn null_and_nan_match_in_any_case() {
for token in ["", " ", "null", "NULL", "Null", "nan", "NaN", "NAN"] {
assert!(is_null_like_token(token), "{token:?}");
}
}
#[test]
fn markers_match_only_in_their_writers_spelling() {
for value in [
"Na", "na", "none", "NONE", "#n/a", "\\n", "N/a", "NA1", "N/A/",
] {
assert!(!is_null_like_token(value), "{value:?}");
}
}
#[test]
fn ambiguous_placeholders_stay_values() {
for value in ["-", "?", "n.d.", "nil", "(null)", "0"] {
assert!(!is_null_like_token(value), "{value:?}");
}
}
}