From f21e6e0280b094efb72736f53931ecf0e7c2c0a2 Mon Sep 17 00:00:00 2001 From: Alexander Batischev Date: Sun, 25 Apr 2021 21:15:04 +0300 Subject: Use an iterator internally for LocalGeneration This adds the machinery. We have to keep the compiled SQL query while the iterator is in use, so we wrap it in an `SqlResults` struct which the iterator borrows. --- src/generation.rs | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index a780203..e5ca14c 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -197,7 +197,7 @@ impl LocalGeneration { } pub fn files(&self) -> LocalGenerationResult>> { - sql::files(&self.conn) + Ok(sql::files(&self.conn)?.iter()?.collect()) } pub fn chunkids(&self, fileno: FileId) -> LocalGenerationResult> { @@ -221,7 +221,7 @@ mod sql { use crate::backup_reason::Reason; use crate::chunkid::ChunkId; use crate::fsentry::FilesystemEntry; - use rusqlite::{params, Connection, OpenFlags, Row, Transaction}; + use rusqlite::{params, Connection, OpenFlags, Row, Statement, Transaction}; use std::os::unix::ffi::OsStrExt; use std::path::Path; @@ -289,24 +289,37 @@ mod sql { Ok(count) } - pub fn files( - conn: &Connection, - ) -> LocalGenerationResult>> { - let mut stmt = conn.prepare("SELECT * FROM files")?; - let iter = stmt.query_map(params![], |row| row_to_entry(row))?; - let mut files: Vec> = vec![]; - for x in iter { - match x { - Ok((fileno, json, reason)) => { - let result = serde_json::from_str(&json) - .map(|entry| BackedUpFile::new(fileno, entry, &reason)) - .map_err(|e| e.into()); - files.push(result) - } - Err(e) => files.push(Err(e.into())), - } + // A pointer to an iterator over values of type `LocalGenerationResult`. The iterator is + // only valid for the lifetime 'stmt. + // + // The fact that it's a pointer (`Box`) means we don't care what the actual type of + // the iterator is, and who produces it. + type SqlResultsIterator<'stmt, T> = Box> + 'stmt>; + + pub struct SqlResults<'conn> { + stmt: Statement<'conn>, + } + + impl<'conn> SqlResults<'conn> { + fn new(conn: &'conn Connection, statement: &str) -> LocalGenerationResult { + let stmt = conn.prepare(statement)?; + Ok(Self { stmt }) + } + + pub fn iter(&'_ mut self) -> LocalGenerationResult> { + let iter = self.stmt.query_map(params![], |row| row_to_entry(row))?; + let iter = iter.map(|x| match x { + Ok((fileno, json, reason)) => serde_json::from_str(&json) + .map(|entry| BackedUpFile::new(fileno, entry, &reason)) + .map_err(|e| e.into()), + Err(e) => Err(e.into()), + }); + Ok(iter) } - Ok(files) + } + + pub fn files(conn: &Connection) -> LocalGenerationResult> { + SqlResults::new(conn, "SELECT * FROM files") } pub fn chunkids(conn: &Connection, fileno: FileId) -> LocalGenerationResult> { -- cgit v1.2.1