Skip to content

Commit

Permalink
Row: it can now be iterated (#106)
Browse files Browse the repository at this point in the history
* Initial row iter() implementation

* Added a test

* Moved .iter() into the correct impl block

* Added column_names to Row

* Return columns in the correct order when iterating on a Row

* Ran cargo clippy --fix

* Hopefully address PR comments

* Address PR comments, part two
  • Loading branch information
eutampieri authored Feb 16, 2025
1 parent 29e0420 commit 3bd2f40
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 5 deletions.
11 changes: 11 additions & 0 deletions src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct CursorWithOwnership<'l> {
/// A row.
#[derive(Debug)]
pub struct Row {
column_names: Rc<Vec<String>>,
column_mapping: Rc<HashMap<String, usize>>,
values: Vec<Value>,
}
Expand Down Expand Up @@ -107,6 +108,7 @@ macro_rules! implement(
match self.try_next() {
Ok(value) => {
value.map(|values| Ok(Row {
column_names: self.statement.column_names.clone(),
column_mapping: self.statement.column_mapping(),
values,
}))
Expand Down Expand Up @@ -186,6 +188,15 @@ impl Row {
}
T::try_from(&self.values[column.index(self)])
}

pub fn iter(&self) -> impl Iterator<Item = (&'_ str, &'_ Value)> + use<'_> {
self.column_names.iter().map(|column_name| {
(
column_name.as_str(),
&self.values[self.column_mapping[column_name]],
)
})
}
}

impl From<Row> for Vec<Value> {
Expand Down
6 changes: 3 additions & 3 deletions src/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ macro_rules! transient(
/// A prepared statement.
pub struct Statement<'l> {
raw: (*mut ffi::sqlite3_stmt, *mut ffi::sqlite3),
column_names: Vec<String>,
pub(crate) column_names: Rc<Vec<String>>,
column_mapping: Rc<HashMap<String, usize>>,
phantom: PhantomData<(ffi::sqlite3_stmt, &'l ffi::sqlite3)>,
}
Expand Down Expand Up @@ -271,7 +271,7 @@ impl<'l> Statement<'l> {
}
}

impl<'l> Drop for Statement<'l> {
impl Drop for Statement<'_> {
#[inline]
fn drop(&mut self) {
unsafe { ffi::sqlite3_finalize(self.raw.0) };
Expand Down Expand Up @@ -603,7 +603,7 @@ where
.collect();
Ok(Statement {
raw: (raw_statement, raw_connection),
column_names,
column_names: Rc::new(column_names),
column_mapping: Rc::new(column_mapping),
phantom: PhantomData,
})
Expand Down
40 changes: 40 additions & 0 deletions tests/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,46 @@ fn try_next_try_into() {
assert!((&row[4]).try_into::<&str>().is_err());
}

#[test]
fn row_can_be_iterated() {
let connection = setup_users(":memory:");
let query = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(query));
let mut cursor = statement.iter();
let row = ok!(ok!(cursor.next()));
let _ = row.iter();
}

#[test]
fn iterating_row_yields_all_items() {
let connection = setup_users(":memory:");
let query = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(query));
let mut cursor = statement.iter();
let row = ok!(ok!(cursor.next()));
let row = row.iter();
assert_eq!(5, row.count());
}

#[test]
fn iterating_row_yields_all_items_in_correct_order() {
let connection = setup_users(":memory:");
let query = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(query));
let mut cursor = statement.iter();
let row = ok!(ok!(cursor.next()));
let mut row = row.iter();
assert_eq!(Some(("id", &Value::Integer(1))), row.next());
assert_eq!(
Some(("name", &Value::String("Alice".to_owned()))),
row.next()
);
assert_eq!(Some(("age", &Value::Float(42.69))), row.next());
assert_eq!(Some(("photo", &Value::Binary(vec![66, 105]))), row.next());
assert_eq!(Some(("email", &Value::Null)), row.next());
assert_eq!(None, row.next());
}

#[test]
fn workflow() {
let connection = setup_users(":memory:");
Expand Down
4 changes: 2 additions & 2 deletions tests/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ fn workflow_1() {
statement: Statement<'l>,
}

impl<'l> Database<'l> {
impl Database<'_> {
fn run_once(&mut self) -> sqlite::Result<()> {
self.statement.reset()?;
self.statement.bind((":age", 40))?;
Expand All @@ -269,7 +269,7 @@ fn workflow_1() {

let mut database = Database {
connection: &connection,
statement: statement,
statement,
};

for _ in 0..5 {
Expand Down

0 comments on commit 3bd2f40

Please sign in to comment.