use rusqlite::Connection; use error::{Result, LinoError}; #[derive(Serialize, Debug, Clone)] pub struct Quote { id: i64, date: String, author: String, score: String, votes: u32, content: String, } pub fn init(c: &Connection) -> Result<()> { info!("Initializing db"); try!(c.execute_batch( r#" CREATE TABLE quotes ( id INTEGER PRIMARY KEY AUTOINCREMENT, approved BOOL DEFAULT false, timestamp DATETIME NOT NULL, author TEXT NOT NULL, content TEXT NOT NULL, CONSTRAINT unique_content UNIQUE (timestamp, content) ); CREATE TABLE votes ( id INTEGER PRIMARY KEY AUTOINCREMENT, quote_id INTEGER NOT NULL, score INTEGER NOT NULL, FOREIGN KEY(quote_id) REFERENCES quotes(id) ); "#, )); Ok(()) } pub fn populate_test_db(c: &Connection) -> Result<()> { info!("Populating test db"); try!(c.execute_batch( r#" INSERT INTO quotes (id, approved, timestamp, author, content) VALUES (1, 1, '2017-07-09', 'orbekk', 'test quote'), (2, 1, '2017-07-09', 'orbekk', 'test quote2'), (3, 0, '2017-07-09', 'orbekk', 'test quote3'); INSERT INTO votes (quote_id, score) VALUES (1, 2), (1, 3), (1, 1), (2, 3), (2, 4); "#, )); Ok(()) } fn internal_get_quotes(c: &Connection, id: Option) -> Result> { let mut stmt = c.prepare( r#" SELECT q.id, q.timestamp, q.author, q.content, sum(v.score), count(v.score) FROM quotes q JOIN votes v ON (q.id = v.quote_id) WHERE (?1 AND q.id = ?2) OR (NOT ?1 AND q.approved) GROUP BY 1, 2, 3, 4; ORDER BY q.id DESC; "#, )?; let rows = stmt.query_map(&[&id.is_some(), &id.unwrap_or(-1)], |row| { Quote { id: row.get(0), date: row.get(1), author: row.get(2), content: row.get(3), score: format!("{:.2}", row.get::(4) / row.get::(5)), votes: row.get(5), } })?; let result = rows.map(|r| r.map_err(|e| From::from(e))) .collect::>>(); // For some reason, the ordering from sqlite doesn't work. result.map(|mut queries| { queries.sort_by_key(|q| -q.id); queries }) } pub fn get_quotes(c: &Connection) -> Result> { internal_get_quotes(c, None) } pub fn get_quote(c: &Connection, id: i64) -> Result { let quotes = internal_get_quotes(c, Some(id))?; quotes.into_iter().next().ok_or(LinoError::NotFound( format!("quote with id {}", id), )) }