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