| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 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}; | 
| Janis Danisevskis | 4522c2b | 2020-11-27 18:04:58 -0800 | [diff] [blame] | 17 | use rusqlite::{types::FromSql, Row, Rows}; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 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. | 
|  | 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 |  | 
|  | 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 | } | 
| Janis Danisevskis | 4522c2b | 2020-11-27 18:04:58 -0800 | [diff] [blame] | 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. | 
|  | 61 | 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. | 
|  | 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 | } |