1use std::collections::HashMap;
2
3use crate::error::{Error, Result};
4
5pub trait FromRow: Sized {
10 fn from_row(row: &libsql::Row) -> Result<Self>;
16}
17
18pub struct ColumnMap {
23 map: HashMap<String, i32>,
24}
25
26impl ColumnMap {
27 pub fn from_row(row: &libsql::Row) -> Self {
29 let count = row.column_count();
30 let mut map = HashMap::with_capacity(count as usize);
31 for i in 0..count {
32 if let Some(name) = row.column_name(i) {
33 map.insert(name.to_string(), i);
34 }
35 }
36 Self { map }
37 }
38
39 pub fn index(&self, name: &str) -> Result<i32> {
47 self.map
48 .get(name)
49 .copied()
50 .ok_or_else(|| Error::internal(format!("column not found: {name}")))
51 }
52
53 pub fn get<T: FromValue>(&self, row: &libsql::Row, name: &str) -> Result<T> {
65 let idx = self.index(name)?;
66 let val = row.get_value(idx).map_err(Error::from)?;
67 T::from_value(val)
68 }
69}
70
71pub trait FromValue: Sized {
79 fn from_value(val: libsql::Value) -> Result<Self>;
85}
86
87impl FromValue for libsql::Value {
88 fn from_value(val: libsql::Value) -> Result<Self> {
89 Ok(val)
90 }
91}
92
93impl FromValue for String {
94 fn from_value(val: libsql::Value) -> Result<Self> {
95 match val {
96 libsql::Value::Text(s) => Ok(s),
97 libsql::Value::Null => Err(Error::internal("unexpected null value")),
98 _ => Err(Error::internal("invalid column type: expected text")),
99 }
100 }
101}
102
103impl FromValue for i32 {
104 fn from_value(val: libsql::Value) -> Result<Self> {
105 match val {
106 libsql::Value::Integer(i) => {
107 i32::try_from(i).map_err(|_| Error::internal("integer out of i32 range"))
108 }
109 libsql::Value::Null => Err(Error::internal("unexpected null value")),
110 _ => Err(Error::internal("invalid column type: expected integer")),
111 }
112 }
113}
114
115impl FromValue for u32 {
116 fn from_value(val: libsql::Value) -> Result<Self> {
117 match val {
118 libsql::Value::Integer(i) => {
119 u32::try_from(i).map_err(|_| Error::internal("integer out of u32 range"))
120 }
121 libsql::Value::Null => Err(Error::internal("unexpected null value")),
122 _ => Err(Error::internal("invalid column type: expected integer")),
123 }
124 }
125}
126
127impl FromValue for i64 {
128 fn from_value(val: libsql::Value) -> Result<Self> {
129 match val {
130 libsql::Value::Integer(i) => Ok(i),
131 libsql::Value::Null => Err(Error::internal("unexpected null value")),
132 _ => Err(Error::internal("invalid column type: expected integer")),
133 }
134 }
135}
136
137impl FromValue for u64 {
138 fn from_value(val: libsql::Value) -> Result<Self> {
139 match val {
140 libsql::Value::Integer(i) => {
141 u64::try_from(i).map_err(|_| Error::internal("integer out of u64 range"))
142 }
143 libsql::Value::Null => Err(Error::internal("unexpected null value")),
144 _ => Err(Error::internal("invalid column type: expected integer")),
145 }
146 }
147}
148
149impl FromValue for f64 {
150 fn from_value(val: libsql::Value) -> Result<Self> {
151 match val {
152 libsql::Value::Real(f) => Ok(f),
153 libsql::Value::Integer(i) => Ok(i as f64),
154 libsql::Value::Null => Err(Error::internal("unexpected null value")),
155 _ => Err(Error::internal("invalid column type: expected real")),
156 }
157 }
158}
159
160impl FromValue for bool {
161 fn from_value(val: libsql::Value) -> Result<Self> {
162 match val {
163 libsql::Value::Integer(0) => Ok(false),
164 libsql::Value::Integer(_) => Ok(true),
165 libsql::Value::Null => Err(Error::internal("unexpected null value")),
166 _ => Err(Error::internal("invalid column type: expected integer")),
167 }
168 }
169}
170
171impl FromValue for Vec<u8> {
172 fn from_value(val: libsql::Value) -> Result<Self> {
173 match val {
174 libsql::Value::Blob(b) => Ok(b),
175 libsql::Value::Null => Err(Error::internal("unexpected null value")),
176 _ => Err(Error::internal("invalid column type: expected blob")),
177 }
178 }
179}
180
181impl<T: FromValue> FromValue for Option<T> {
182 fn from_value(val: libsql::Value) -> Result<Self> {
183 match val {
184 libsql::Value::Null => Ok(None),
185 other => T::from_value(other).map(Some),
186 }
187 }
188}