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}