1use std::{
2 collections::BTreeMap,
3 convert::From,
4 error::Error,
5 fmt::{self, Display, Formatter},
6 panic,
7 str::FromStr,
8};
9
10use log::Level;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14pub type RequestId = u32;
15
16pub type Requests = Option<BTreeMap<RequestId, Request>>;
17pub type Responses = Option<BTreeMap<RequestId, u32>>;
18
19#[derive(Serialize, Deserialize, Debug, Clone)]
20pub enum SqliteValue {
21 Null,
22 Integer(i64),
23 Real(f64),
24 Text(String),
25 Blob(Vec<u8>),
26}
27
28#[derive(Serialize, Deserialize, Debug)]
29pub struct Row(Vec<SqliteValue>);
30
31impl From<Vec<SqliteValue>> for Row {
32 fn from(v: Vec<SqliteValue>) -> Self {
33 Self(v)
34 }
35}
36
37impl FromIterator<SqliteValue> for Row {
38 fn from_iter<T: IntoIterator<Item = SqliteValue>>(iter: T) -> Self {
39 Self(iter.into_iter().collect())
40 }
41}
42
43#[derive(Serialize, Deserialize, Debug)]
44pub enum Request {
45 Query {
46 sql: String,
47 params: Vec<SqliteValue>,
48 },
49 Exec {
50 sql: String,
51 params: Vec<SqliteValue>,
52 },
53}
54
55#[derive(Serialize, Deserialize, Debug)]
56pub struct QueryResponse {
57 pub columns: Vec<String>,
58 pub rows: Vec<Row>,
59}
60
61#[derive(Serialize, Deserialize, Debug)]
62pub struct ExecResponse {
63 pub changes: usize,
64}
65
66#[derive(Serialize, Deserialize, Debug, Error)]
67pub enum ErrorResponse {
68 #[error("SQLite Error({code}): {message}")]
69 SqliteError { code: i32, message: String },
70 #[error("Unknown: {0}")]
71 Unknown(String),
72}
73
74#[derive(Serialize, Deserialize)]
75pub struct LogRecord {
76 level: String,
77 message: String,
78 file: Option<String>,
79 line: Option<u32>,
80}
81
82impl From<&panic::PanicInfo<'_>> for LogRecord {
83 fn from(info: &panic::PanicInfo) -> Self {
84 let loc = info.location();
85 LogRecord {
86 level: log::Level::Error.to_string(),
87 message: info.to_string(),
88 file: loc.map(|l| l.file().to_string()),
89 line: loc.map(|l| l.line()),
90 }
91 }
92}
93
94impl From<&log::Record<'_>> for LogRecord {
95 fn from(record: &log::Record) -> Self {
96 Self {
97 level: record.level().to_string(),
98 message: record.args().to_string(),
99 file: record.file().map(|s| s.to_string()),
100 line: record.line(),
101 }
102 }
103}
104
105impl LogRecord {
106 pub fn log(&self) {
107 log::logger().log(
108 &log::Record::builder()
109 .level(Level::from_str(&self.level).unwrap_or(Level::Error))
110 .file(self.file.as_deref())
111 .line(self.line)
112 .module_path(Some("wasm guest"))
113 .args(format_args!("{}", self.message))
114 .build(),
115 );
116 }
117}
118
119#[derive(Serialize, Deserialize, Debug)]
120pub enum ReducerError {
121 ConversionError {
122 value: SqliteValue,
123 target_type: String,
124 },
125
126 Unknown(String),
127}
128
129impl Display for ReducerError {
130 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
131 write!(f, "ReducerError: {:?}", self)
132 }
133}
134
135impl<E: Error> From<E> for ReducerError {
136 fn from(e: E) -> Self {
137 Self::Unknown(e.to_string())
138 }
139}
140
141impl Row {
144 pub fn get<'a, T>(&'a self, idx: usize) -> Result<T, ReducerError>
145 where
146 T: TryFrom<&'a SqliteValue, Error = ReducerError>,
147 {
148 T::try_from(self.get_value(idx))
149 }
150
151 pub fn maybe_get<'a, T>(&'a self, idx: usize) -> Result<Option<T>, ReducerError>
152 where
153 T: TryFrom<&'a SqliteValue, Error = ReducerError>,
154 {
155 match self.get_value(idx) {
156 SqliteValue::Null => Ok(None),
157 v => Ok(Some(T::try_from(v)?)),
158 }
159 }
160
161 pub fn get_value(&self, idx: usize) -> &SqliteValue {
162 self.0.get(idx).expect("row index out of bounds")
163 }
164}
165
166macro_rules! impl_types_for_sqlvalue {
167 ($e:path, $($t:ty),*) => {
168 $(
169 impl From<$t> for SqliteValue {
170 fn from(t: $t) -> Self {
171 $e(t.into())
172 }
173 }
174
175 impl TryFrom<&SqliteValue> for $t {
176 type Error = ReducerError;
177
178 fn try_from(value: &SqliteValue) -> Result<Self, Self::Error> {
179 match value {
180 $e(i) => Ok(*i as $t),
181 v => Err(ReducerError::ConversionError {
182 value: v.clone(),
183 target_type: stringify!($t).to_owned(),
184 }),
185 }
186 }
187 }
188 )*
189 };
190}
191
192impl_types_for_sqlvalue!(SqliteValue::Integer, i8, i16, i32, i64);
193impl_types_for_sqlvalue!(SqliteValue::Real, f32, f64);
194
195impl<T> From<Option<T>> for SqliteValue
196where
197 SqliteValue: From<T>,
198{
199 fn from(o: Option<T>) -> Self {
200 match o {
201 Some(t) => Self::from(t),
202 None => Self::Null,
203 }
204 }
205}
206
207impl From<Vec<u8>> for SqliteValue {
208 fn from(b: Vec<u8>) -> Self {
209 Self::Blob(b)
210 }
211}
212
213impl TryFrom<&SqliteValue> for Vec<u8> {
214 type Error = ReducerError;
215
216 fn try_from(value: &SqliteValue) -> Result<Self, Self::Error> {
217 match value {
218 SqliteValue::Blob(b) => Ok(b.clone()),
219 v => Err(ReducerError::ConversionError {
220 value: v.clone(),
221 target_type: "Vec<u8>".to_owned(),
222 }),
223 }
224 }
225}
226
227impl From<&str> for SqliteValue {
228 fn from(s: &str) -> Self {
229 Self::Text(s.to_string())
230 }
231}
232
233impl<'a> TryFrom<&'a SqliteValue> for &'a str {
234 type Error = ReducerError;
235
236 fn try_from(value: &SqliteValue) -> Result<&str, Self::Error> {
237 match value {
238 SqliteValue::Text(s) => Ok(s.as_str()),
239 v => Err(ReducerError::ConversionError {
240 value: v.clone(),
241 target_type: "&str".to_owned(),
242 }),
243 }
244 }
245}
246
247impl From<String> for SqliteValue {
248 fn from(s: String) -> Self {
249 Self::Text(s)
250 }
251}
252
253impl TryFrom<&SqliteValue> for String {
254 type Error = ReducerError;
255
256 fn try_from(value: &SqliteValue) -> Result<Self, Self::Error> {
257 match value {
258 SqliteValue::Text(s) => Ok(s.clone()),
259 v => Err(ReducerError::ConversionError {
260 value: v.clone(),
261 target_type: "String".to_owned(),
262 }),
263 }
264 }
265}
266
267impl From<bool> for SqliteValue {
268 fn from(b: bool) -> Self {
269 Self::Integer(b as i64)
270 }
271}
272
273impl TryFrom<&SqliteValue> for bool {
274 type Error = ReducerError;
275
276 fn try_from(value: &SqliteValue) -> Result<Self, Self::Error> {
277 match value {
278 SqliteValue::Integer(i) => Ok(*i != 0),
279 v => Err(ReducerError::ConversionError {
280 value: v.clone(),
281 target_type: "bool".to_owned(),
282 }),
283 }
284 }
285}