Skip to main content

sqlite_diff_rs/encoding/serial/
display.rs

1//! Display implementation for Value as SQL literals.
2
3use super::Value;
4
5impl<S: AsRef<str>, B: AsRef<[u8]>> core::fmt::Display for Value<S, B> {
6    /// Format a Value as a SQL literal.
7    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
8        match self {
9            Value::Integer(v) => write!(f, "{v}"),
10            Value::Real(v) => {
11                if v.is_nan() {
12                    write!(f, "NULL")
13                } else if v.is_infinite() {
14                    if v.is_sign_positive() {
15                        write!(f, "9e999") // SQLite's way of representing +infinity
16                    } else {
17                        write!(f, "-9e999")
18                    }
19                } else {
20                    write!(f, "{v}")
21                }
22            }
23            Value::Text(s) => {
24                // Escape single quotes by doubling them
25                write!(f, "'")?;
26                for c in s.as_ref().chars() {
27                    if c == '\'' {
28                        write!(f, "''")?;
29                    } else {
30                        core::fmt::Write::write_char(f, c)?;
31                    }
32                }
33                write!(f, "'")
34            }
35            Value::Blob(b) => {
36                write!(f, "X'")?;
37                for byte in b.as_ref() {
38                    write!(f, "{byte:02X}")?;
39                }
40                write!(f, "'")
41            }
42            Value::Null => write!(f, "NULL"),
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::Value;
50    use alloc::format;
51    use alloc::string::String;
52    use alloc::vec;
53    use alloc::vec::Vec;
54
55    type TestValue = Value<String, Vec<u8>>;
56
57    #[test]
58    fn test_display_null() {
59        let v: TestValue = Value::Null;
60        assert_eq!(format!("{v}"), "NULL");
61    }
62
63    #[test]
64    fn test_display_integer() {
65        let v: TestValue = Value::Integer(42);
66        assert_eq!(format!("{v}"), "42");
67
68        let v: TestValue = Value::Integer(-1);
69        assert_eq!(format!("{v}"), "-1");
70
71        let v: TestValue = Value::Integer(i64::MAX);
72        assert_eq!(format!("{v}"), "9223372036854775807");
73
74        let v: TestValue = Value::Integer(i64::MIN);
75        assert_eq!(format!("{v}"), "-9223372036854775808");
76    }
77
78    #[test]
79    #[allow(clippy::approx_constant)]
80    fn test_display_real_finite() {
81        let v: TestValue = Value::Real(3.14);
82        assert_eq!(format!("{v}"), "3.14");
83
84        let v: TestValue = Value::Real(-0.0);
85        let s = format!("{v}");
86        assert!(s == "-0" || s == "0", "got {s}");
87    }
88
89    #[test]
90    fn test_display_real_nan_becomes_null() {
91        let v: TestValue = Value::Real(f64::NAN);
92        assert_eq!(format!("{v}"), "NULL");
93    }
94
95    #[test]
96    fn test_display_real_positive_infinity() {
97        let v: TestValue = Value::Real(f64::INFINITY);
98        assert_eq!(format!("{v}"), "9e999");
99    }
100
101    #[test]
102    fn test_display_real_negative_infinity() {
103        let v: TestValue = Value::Real(f64::NEG_INFINITY);
104        assert_eq!(format!("{v}"), "-9e999");
105    }
106
107    #[test]
108    fn test_display_text_plain() {
109        let v: TestValue = Value::Text("hello".into());
110        assert_eq!(format!("{v}"), "'hello'");
111    }
112
113    #[test]
114    fn test_display_text_empty() {
115        let v: TestValue = Value::Text(String::new());
116        assert_eq!(format!("{v}"), "''");
117    }
118
119    #[test]
120    fn test_display_text_escapes_single_quote() {
121        let v: TestValue = Value::Text("it's".into());
122        assert_eq!(format!("{v}"), "'it''s'");
123    }
124
125    #[test]
126    fn test_display_text_escapes_multiple_quotes() {
127        let v: TestValue = Value::Text("''".into());
128        assert_eq!(format!("{v}"), "''''''");
129    }
130
131    #[test]
132    fn test_display_blob_uppercase_hex() {
133        let v: TestValue = Value::Blob(vec![0x01, 0xAB, 0xFF]);
134        assert_eq!(format!("{v}"), "X'01ABFF'");
135    }
136
137    #[test]
138    fn test_display_blob_empty() {
139        let v: TestValue = Value::Blob(Vec::new());
140        assert_eq!(format!("{v}"), "X''");
141    }
142}