rbdc_pg/types/
tsquery.rs

1use crate::types::decode::Decode;
2use crate::types::encode::{Encode, IsNull};
3use crate::value::{PgValue, PgValueFormat};
4use rbdc::Error;
5use rbs::Value;
6
7/// PostgreSQL TSQUERY type for full-text search queries
8///
9/// TSQUERY stores a lexeme search query for full-text search.
10/// In most cases, you'll use this as TEXT and let PostgreSQL handle the full-text search.
11#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
12pub struct TsQuery(pub String);
13
14impl From<String> for TsQuery {
15    fn from(value: String) -> Self {
16        Self(value)
17    }
18}
19
20impl From<&str> for TsQuery {
21    fn from(value: &str) -> Self {
22        Self(value.to_string())
23    }
24}
25
26impl From<TsQuery> for Value {
27    fn from(arg: TsQuery) -> Self {
28        rbs::Value::Ext("tsquery", Box::new(rbs::Value::String(arg.0)))
29    }
30}
31
32impl std::fmt::Display for TsQuery {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.0)
35    }
36}
37
38impl Decode for TsQuery {
39    fn decode(value: PgValue) -> Result<Self, Error> {
40        Ok(match value.format() {
41            PgValueFormat::Binary => {
42                // TSQUERY binary format is complex
43                // For now, treat it as TEXT and return the raw bytes as a string
44                // Applications should use TEXT format or PostgreSQL functions
45                let bytes = value.as_bytes()?;
46                Self(String::from_utf8_lossy(bytes).to_string())
47            }
48            PgValueFormat::Text => {
49                let s = value.as_str()?;
50                Self(s.to_string())
51            }
52        })
53    }
54}
55
56impl Encode for TsQuery {
57    fn encode(self, _buf: &mut crate::arguments::PgArgumentBuffer) -> Result<IsNull, Error> {
58        // TSQUERY encoding is complex
59        // Applications should use PostgreSQL's to_tsquery() or plainto_tsquery() functions instead
60        Err(Error::from(
61            "TsQuery encoding not supported. Use PostgreSQL's to_tsquery() function in your query instead."
62        ))
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::types::decode::Decode;
70    use crate::value::{PgValue, PgValueFormat};
71
72    #[test]
73    fn test_display() {
74        let tsquery = TsQuery("hello & world".to_string());
75        assert_eq!(format!("{}", tsquery), "hello & world");
76    }
77
78    #[test]
79    fn test_from_str() {
80        let tsquery = TsQuery::from("search & query");
81        assert_eq!(tsquery.0, "search & query");
82    }
83
84    #[test]
85    fn test_decode_text() {
86        let s = "hello & world";
87        let result: TsQuery = Decode::decode(PgValue {
88            value: Some(s.as_bytes().to_vec()),
89            type_info: crate::type_info::PgTypeInfo::UNKNOWN,
90            format: PgValueFormat::Text,
91            timezone_sec: None,
92        }).unwrap();
93        assert_eq!(result.0, "hello & world");
94    }
95
96    #[test]
97    fn test_from_value() {
98        let tsquery = TsQuery("test".to_string());
99        let value: rbs::Value = tsquery.into();
100        match value {
101            rbs::Value::Ext(type_name, boxed) => {
102                assert_eq!(type_name, "tsquery");
103                if let rbs::Value::String(s) = *boxed {
104                    assert_eq!(s, "test");
105                } else {
106                    panic!("Expected String value");
107                }
108            }
109            _ => panic!("Expected Ext variant"),
110        }
111    }
112}