1 // Copyright 2020, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::error::Error as KsError;
16 use anyhow::{Context, Result};
17 use rusqlite::{types::FromSql, Row, Rows};
18
19 // Takes Rows as returned by a query call on prepared statement.
20 // Extracts exactly one row with the `row_extractor` and fails if more
21 // rows are available.
22 // If no row was found, `None` is passed to the `row_extractor`.
23 // This allows the row extractor to decide on an error condition or
24 // a different default behavior.
with_rows_extract_one<'a, T, F>(rows: &mut Rows<'a>, row_extractor: F) -> Result<T> where F: FnOnce(Option<&Row<'a>>) -> Result<T>,25 pub fn with_rows_extract_one<'a, T, F>(rows: &mut Rows<'a>, row_extractor: F) -> Result<T>
26 where
27 F: FnOnce(Option<&Row<'a>>) -> Result<T>,
28 {
29 let result =
30 row_extractor(rows.next().context("with_rows_extract_one: Failed to unpack row.")?);
31
32 rows.next()
33 .context("In with_rows_extract_one: Failed to unpack unexpected row.")?
34 .map_or_else(|| Ok(()), |_| Err(KsError::sys()))
35 .context("In with_rows_extract_one: Unexpected row.")?;
36
37 result
38 }
39
with_rows_extract_all<'a, F>(rows: &mut Rows<'a>, mut row_extractor: F) -> Result<()> where F: FnMut(&Row<'a>) -> Result<()>,40 pub fn with_rows_extract_all<'a, F>(rows: &mut Rows<'a>, mut row_extractor: F) -> Result<()>
41 where
42 F: FnMut(&Row<'a>) -> Result<()>,
43 {
44 loop {
45 match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
46 Some(row) => {
47 row_extractor(&row).context("In with_rows_extract_all.")?;
48 }
49 None => break Ok(()),
50 }
51 }
52 }
53
54 /// This struct is defined to postpone converting rusqlite column value to the
55 /// appropriate key parameter value until we know the corresponding tag value.
56 /// Wraps the column index and a rusqlite row.
57 pub struct SqlField<'a>(usize, &'a Row<'a>);
58
59 impl<'a> SqlField<'a> {
60 /// Creates a new SqlField with the given index and row.
new(index: usize, row: &'a Row<'a>) -> Self61 pub fn new(index: usize, row: &'a Row<'a>) -> Self {
62 Self(index, row)
63 }
64 /// Returns the column value from the row, when we know the expected type.
get<T: FromSql>(&self) -> rusqlite::Result<T>65 pub fn get<T: FromSql>(&self) -> rusqlite::Result<T> {
66 self.1.get(self.0)
67 }
68 }
69
70 /// This macro implements two types to aid in the implementation of a type safe metadata
71 /// store. The first is a collection of metadata and the second is the entry in that
72 /// collection. The caller has to provide the infrastructure to load and store the
73 /// the collection or individual entries in a SQLite database. The idea is that once
74 /// the infrastructure for a metadata collection is in place all it takes to add another
75 /// field is make a new entry in the list of variants (see details below).
76 ///
77 /// # Usage
78 /// ```
79 /// impl_metadata!{
80 /// /// This is the name of the collection.
81 /// #[derive(Debug, Default)]
82 /// pub struct CollectionName;
83 /// /// This is the name of the Entry type followed by a list of variants, accessor function
84 /// /// names, and types.
85 /// #[derive(Debug, Eq, PartialEq)]
86 /// pub enum EntryName {
87 /// /// An enum variant with an accessor function name.
88 /// VariantA(u32) with accessor get_variant_a,
89 /// /// A second variant. `MyType` must implement rusqlite::types::ToSql and FromSql.
90 /// VariantB(MyType) with accessor get_variant_b,
91 /// // --- ADD NEW META DATA FIELDS HERE ---
92 /// // For backwards compatibility add new entries only to
93 /// // end of this list and above this comment.
94 /// };
95 /// }
96 /// ```
97 ///
98 /// expands to:
99 ///
100 /// ```
101 /// pub enum EntryName {
102 /// VariantA(u32),
103 /// VariantB(MyType),
104 /// }
105 ///
106 /// impl EntryName {}
107 /// /// Returns a numeric variant id that can be used for persistent storage.
108 /// fn db_tag(&self) -> i64 {...}
109 /// /// Helper function that constructs a new `EntryName` given a variant identifier
110 /// /// and a to-be-extracted `SqlFiled`
111 /// fn new_from_sql(db_tag: i64, data: &SqlField) -> Result<Self> {...}
112 /// }
113 ///
114 /// impl ToSql for EntryName {...}
115 ///
116 /// pub struct CollectionName {
117 /// data: std::collections::HashMap<i64, EntryName>,
118 /// }
119 ///
120 /// impl CollectionName {
121 /// /// Create a new collection of meta data.
122 /// pub fn new() -> Self {...}
123 /// /// Add a new entry to this collection. Replaces existing entries of the
124 /// /// same variant unconditionally.
125 /// pub fn add(&mut self, e: EntryName) {...}
126 /// /// Type safe accessor function for the defined fields.
127 /// pub fn get_variant_a() -> Option<u32> {...}
128 /// pub fn get_variant_b() -> Option<MyType> {...}
129 /// }
130 ///
131 /// let mut collection = CollectionName::new();
132 /// collection.add(EntryName::VariantA(3));
133 /// let three: u32 = collection.get_variant_a().unwrap()
134 /// ```
135 ///
136 /// The caller of this macro must implement the actual database queries to load and store
137 /// either a whole collection of metadata or individual fields. For example by associating
138 /// with the given type:
139 /// ```
140 /// impl CollectionName {
141 /// fn load(tx: &Transaction) -> Result<Self> {...}
142 /// }
143 /// ```
144 #[macro_export]
145 macro_rules! impl_metadata {
146 // These two macros assign incrementing numeric ids to each field which are used as
147 // database tags.
148 (@gen_consts {} {$($n:ident $nid:tt,)*} {$($count:tt)*}) => {
149 $(
150 // This allows us to reuse the variant name for these constants. The constants
151 // are private so that this exception does not spoil the public interface.
152 #[allow(non_upper_case_globals)]
153 const $n: i64 = $nid;
154 )*
155 };
156 (@gen_consts {$first:ident $(,$tail:ident)*} {$($out:tt)*} {$($count:tt)*}) => {
157 impl_metadata!(@gen_consts {$($tail),*} {$($out)* $first ($($count)*),} {$($count)* + 1});
158 };
159 (
160 $(#[$nmeta:meta])*
161 $nvis:vis struct $name:ident;
162 $(#[$emeta:meta])*
163 $evis:vis enum $entry:ident {
164 $($(#[$imeta:meta])* $vname:ident($t:ty) with accessor $func:ident),* $(,)?
165 };
166 ) => {
167 $(#[$emeta])*
168 $evis enum $entry {
169 $(
170 $(#[$imeta])*
171 $vname($t),
172 )*
173 }
174
175 impl $entry {
176 fn db_tag(&self) -> i64 {
177 match self {
178 $(Self::$vname(_) => $name::$vname,)*
179 }
180 }
181
182 fn new_from_sql(db_tag: i64, data: &SqlField) -> anyhow::Result<Self> {
183 match db_tag {
184 $(
185 $name::$vname => {
186 Ok($entry::$vname(
187 data.get()
188 .with_context(|| format!(
189 "In {}::new_from_sql: Unable to get {}.",
190 stringify!($entry),
191 stringify!($vname)
192 ))?
193 ))
194 },
195 )*
196 _ => Err(anyhow!(format!(
197 "In {}::new_from_sql: unknown db tag {}.",
198 stringify!($entry), db_tag
199 ))),
200 }
201 }
202 }
203
204 impl rusqlite::types::ToSql for $entry {
205 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
206 match self {
207 $($entry::$vname(v) => v.to_sql(),)*
208 }
209 }
210 }
211
212 $(#[$nmeta])*
213 $nvis struct $name {
214 data: std::collections::HashMap<i64, $entry>,
215 }
216
217 impl $name {
218 /// Create a new instance of $name
219 pub fn new() -> Self {
220 Self{data: std::collections::HashMap::new()}
221 }
222
223 impl_metadata!{@gen_consts {$($vname),*} {} {0}}
224
225 /// Add a new instance of $entry to this collection of metadata.
226 pub fn add(&mut self, entry: $entry) {
227 self.data.insert(entry.db_tag(), entry);
228 }
229 $(
230 /// If the variant $vname is set, returns the wrapped value or None otherwise.
231 pub fn $func(&self) -> Option<&$t> {
232 if let Some($entry::$vname(v)) = self.data.get(&Self::$vname) {
233 Some(v)
234 } else {
235 None
236 }
237 }
238 )*
239 }
240 };
241 }
242