• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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