1use crate::types::decode::Decode;
2use crate::types::encode::{Encode, IsNull};
3use crate::value::{PgValueFormat, PgValueRef};
4use rbdc::Error;
5use rbs::Value;
6use std::collections::HashMap;
7use std::fmt::{Display, Formatter};
8
9#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
30pub struct Hstore(pub HashMap<String, String>);
31
32impl Default for Hstore {
33 fn default() -> Self {
34 Self(HashMap::new())
35 }
36}
37
38impl Display for Hstore {
39 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
40 let pairs: Vec<String> = self
41 .0
42 .iter()
43 .map(|(k, v)| format!("{}=>{}", k, v))
44 .collect();
45 write!(f, "{}", pairs.join(", "))
46 }
47}
48
49impl From<HashMap<String, String>> for Hstore {
50 fn from(map: HashMap<String, String>) -> Self {
51 Self(map)
52 }
53}
54
55impl From<Hstore> for Value {
56 fn from(arg: Hstore) -> Self {
57 let s = format!("{}", arg);
59 Value::Ext("hstore", Box::new(Value::String(s)))
60 }
61}
62
63impl Decode for Hstore {
64 fn decode(value: PgValueRef) -> Result<Self, Error> {
65 Ok(match value.format() {
66 PgValueFormat::Binary => {
67 let bytes = value.as_bytes()?;
75 if bytes.len() < 4 {
76 return Err(Error::from("HSTORE binary data too short"));
77 }
78
79 let mut buf = &bytes[..];
80 use byteorder::{BigEndian, ReadBytesExt};
81
82 let count = buf.read_i32::<BigEndian>()? as usize;
83 let mut map = HashMap::new();
84
85 for _ in 0..count {
86 if buf.len() < 8 {
87 return Err(Error::from("HSTORE binary entry too short"));
88 }
89
90 let key_len = buf.read_i32::<BigEndian>()? as usize;
91 let val_len = buf.read_i32::<BigEndian>()? as i32;
92
93 if buf.len() < key_len {
94 return Err(Error::from("HSTORE binary key too short"));
95 }
96
97 let key = String::from_utf8(buf[..key_len].to_vec())
98 .map_err(|e| Error::from(format!("Invalid HSTORE key: {}", e)))?;
99 buf = &buf[key_len..];
100
101 if val_len < 0 {
102 map.insert(key, "null".to_string());
104 } else {
105 let val_len = val_len as usize;
106 if buf.len() < val_len {
107 return Err(Error::from("HSTORE binary value too short"));
108 }
109
110 let val = String::from_utf8(buf[..val_len].to_vec())
111 .map_err(|e| Error::from(format!("Invalid HSTORE value: {}", e)))?;
112 buf = &buf[val_len..];
113
114 map.insert(key, val);
115 }
116 }
117
118 Self(map)
119 }
120 PgValueFormat::Text => {
121 let s = value.as_str()?.trim();
123 if s.is_empty() {
124 return Ok(Self(HashMap::new()));
125 }
126
127 let mut map = HashMap::new();
128 for pair in s.split(',') {
130 let pair = pair.trim();
131 if pair.is_empty() {
132 continue;
133 }
134
135 if let Some(pos) = pair.find("=>") {
137 let key = pair[..pos].trim().to_string();
138 let value = pair[pos + 2..].trim().to_string();
139 map.insert(key, value);
140 } else {
141 return Err(Error::from(format!(
142 "Invalid HSTORE format: '{}'. Expected 'key=>value'",
143 pair
144 )));
145 }
146 }
147
148 Self(map)
149 }
150 })
151 }
152}
153
154impl Encode for Hstore {
155 fn encode(self, _buf: &mut crate::arguments::PgArgumentBuffer) -> Result<IsNull, Error> {
156 Err(Error::from(
159 "HStore encoding not supported. Use hstore(text) or hstore(text, text) in your query instead."
160 ))
161 }
162}