sqlvec/lib.rs
1use std::{iter::FromIterator, str::FromStr, string::ToString};
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use rusqlite::{
7 self,
8 types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, Value, ValueRef},
9};
10
11/// A generic container for vectors whose contents implement ToString & FromStr.
12///
13/// `SqlVec` implements ToSql & FromSql storing values as `\u{F1}` delimited text, allowing for SQL operations.
14///
15/// # Example
16/// ```
17/// use sqlvec::SqlVec;
18/// use rusqlite::{Error, Connection, params};
19///
20/// let conn = Connection::open_in_memory().unwrap();
21///
22/// // Create a table with a column that uses our custom type.
23/// conn.execute(
24/// "CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, data TEXT);",
25/// [],
26/// ).unwrap();
27///
28/// // Insert a SqlVec into the table.
29/// let values = SqlVec::new(vec!["one".to_string(), "two".to_string()]);
30/// conn.execute(
31/// "INSERT INTO test (data) VALUES (?1)",
32/// params![values],
33/// ).unwrap();
34///
35/// // Retrieve the SqlVec from the table.
36/// let mut stmt = conn.prepare("SELECT data FROM test WHERE id = ?1").unwrap();
37/// let mut rows = stmt.query(params![1]).unwrap();
38/// let row = rows.next().unwrap().unwrap();
39/// let db_values: SqlVec<String> = row.get(0).unwrap();
40///
41/// // Assert that the retrieved SqlVec matches the original.
42/// assert_eq!(values, db_values);
43/// ```
44#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45#[derive(Debug, PartialEq, Eq, Clone)]
46pub struct SqlVec<T: ToString + FromStr>(Vec<T>);
47
48impl<T: ToString + FromStr> SqlVec<T> {
49 /// Creates a new `SqlVec` from an iterable collection of items.
50 ///
51 /// # Example
52 ///
53 /// ```
54 /// use sqlvec::SqlVec;
55 ///
56 /// let vec = SqlVec::new([1, 2, 3]);
57 ///
58 /// ```
59 pub fn new<I: IntoIterator<Item = T>>(items: I) -> Self {
60 let items: Vec<T> = items.into_iter().collect();
61 Self(items)
62 }
63
64 /// Consumes the `SqlVec`, returning its internal vector.
65 ///
66 /// This method allows you to take ownership of the underlying vector contained within the `SqlVec`. After calling `into_inner`, the `SqlVec` cannot be used anymore unless recreated.
67 ///
68 /// # Example
69 ///
70 /// ```
71 /// use sqlvec::SqlVec;
72 ///
73 /// let sql_vec = SqlVec::new(vec![1, 2]);
74 /// let vec = sql_vec.into_inner();
75 ///
76 /// assert_eq!(vec, vec![1, 2]);
77 ///
78 /// ```
79 pub fn into_inner(self) -> Vec<T> {
80 self.0
81 }
82
83 /// Returns a borrowed reference to the internal vector.
84 ///
85 /// # Example
86 ///
87 /// ```
88 /// use sqlvec::SqlVec;
89 ///
90 /// let sql_vec = SqlVec::new(vec![1, 2]);
91 /// let vec_ref = sql_vec.inner();
92 ///
93 ///
94 /// assert_eq!(vec_ref, &vec![1, 2]);
95 ///
96 /// ```
97 pub fn inner(&self) -> &Vec<T> {
98 &self.0
99 }
100}
101
102impl<T: ToString + FromStr> FromIterator<T> for SqlVec<T> {
103 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
104 SqlVec(iter.into_iter().collect())
105 }
106}
107
108impl<T: ToString + FromStr> ToString for SqlVec<T> {
109 fn to_string(&self) -> String {
110 self.0
111 .iter()
112 .map(|s| s.to_string())
113 .collect::<Vec<String>>()
114 .join("\u{F1}")
115 }
116}
117
118impl FromStr for SqlVec<String> {
119 type Err = std::convert::Infallible;
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 let items: Vec<String> = s
123 .split('\u{F1}')
124 .filter_map(|s| s.trim().parse().ok())
125 .collect();
126 Ok(Self(items))
127 }
128}
129
130impl<T: ToString + FromStr> ToSql for SqlVec<T> {
131 fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
132 let items_str = self
133 .0
134 .iter()
135 .map(|s| s.to_string())
136 .collect::<Vec<String>>()
137 .join("\u{F1}");
138 Ok(ToSqlOutput::Owned(Value::Text(items_str)))
139 }
140}
141
142impl<T: ToString + FromStr> FromSql for SqlVec<T> {
143 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
144 let items = value
145 .as_str()?
146 .split('\u{F1}')
147 .filter_map(|s| s.parse().ok())
148 .collect();
149 Ok(SqlVec(items))
150 }
151}
152
153// Manually implemented so `T` does not require default trait
154impl<T: ToString + FromStr> Default for SqlVec<T> {
155 /// Unlike `Vec`, `SqlVec` does not require `T` to implement the `Default` trait.
156 ///
157 /// # Example
158 ///
159 /// ```
160 /// use sqlvec::SqlVec;
161 ///
162 /// use std::str::FromStr;
163 ///
164 /// struct MyType {
165 /// value: i32,
166 /// }
167 ///
168 /// impl ToString for MyType {
169 /// fn to_string(&self) -> String {
170 /// self.value.to_string()
171 /// }
172 /// }
173 ///
174 /// impl FromStr for MyType {
175 /// type Err = std::num::ParseIntError;
176 ///
177 /// fn from_str(s: &str) -> Result<Self, Self::Err> {
178 /// s.parse::<i32>().map(|value| MyType { value })
179 /// }
180 /// }
181 ///
182 /// let default: SqlVec<MyType> = SqlVec::default();
183 ///
184 /// ```
185 fn default() -> Self {
186 Self(Vec::new())
187 }
188}