Keystore 2.0: Revise database.

* Make grants persistent.
* Moved some utility functions to db_utils.rs.
* KeystoreDB::new() now takes a path argument that indicates where the
  the database files are to be expected.
* A new test module test::utils.rs introduces TempDir which creates a
  new temporary directory that is cleaned up with all of its content
  when the TempDir opject is dropped.

Test: keystore2_test
Change-Id: I056c404fd9d592ddc8b531394b1c6cc17a0fd736
diff --git a/keystore2/src/db_utils.rs b/keystore2/src/db_utils.rs
new file mode 100644
index 0000000..615005f
--- /dev/null
+++ b/keystore2/src/db_utils.rs
@@ -0,0 +1,52 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::error::Error as KsError;
+use anyhow::{Context, Result};
+use rusqlite::{Row, Rows};
+
+// Takes Rows as returned by a query call on prepared statement.
+// Extracts exactly one row with the `row_extractor` and fails if more
+// rows are available.
+// If no row was found, `None` is passed to the `row_extractor`.
+// This allows the row extractor to decide on an error condition or
+// a different default behavior.
+pub fn with_rows_extract_one<'a, T, F>(rows: &mut Rows<'a>, row_extractor: F) -> Result<T>
+where
+    F: FnOnce(Option<&Row<'a>>) -> Result<T>,
+{
+    let result =
+        row_extractor(rows.next().context("with_rows_extract_one: Failed to unpack row.")?);
+
+    rows.next()
+        .context("In with_rows_extract_one: Failed to unpack unexpected row.")?
+        .map_or_else(|| Ok(()), |_| Err(KsError::sys()))
+        .context("In with_rows_extract_one: Unexpected row.")?;
+
+    result
+}
+
+pub fn with_rows_extract_all<'a, F>(rows: &mut Rows<'a>, mut row_extractor: F) -> Result<()>
+where
+    F: FnMut(&Row<'a>) -> Result<()>,
+{
+    loop {
+        match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
+            Some(row) => {
+                row_extractor(&row).context("In with_rows_extract_all.")?;
+            }
+            None => break Ok(()),
+        }
+    }
+}