use super::*;
#[derive(Default)]
pub struct RemoveOptions {
pub dry_run: bool,
}
pub struct Remove<'a> {
query: Query<'a>,
options: RemoveOptions,
}
impl<'a> Remove<'a> {
pub fn from_query(query: Query<'a>) -> Self { Self { query, options: RemoveOptions::default() } }
pub fn by_source(source: &'a str, identifier: &'a str) -> Self {
Self::from_query(Query::by_source(source, identifier))
}
pub fn by_author(name: &'a str) -> Self { Self::from_query(Query::by_author(name)) }
pub fn dry_run(mut self) -> Self {
self.options.dry_run = true;
self
}
fn build_paper_ids_sql(paper: &Paper) -> (String, Vec<Option<String>>) {
("SELECT id FROM papers WHERE source = ? AND source_identifier = ?".to_string(), vec![
Some(paper.source.to_string()),
Some(paper.source_identifier.clone()),
])
}
fn build_remove_sql(ids: &[i64]) -> (String, Vec<Option<String>>) {
let ids_str = ids.iter().map(|id| id.to_string()).collect::<Vec<_>>().join(",");
(
format!(
"DELETE FROM authors WHERE paper_id IN ({0});
DELETE FROM files WHERE paper_id IN ({0});
DELETE FROM papers WHERE id IN ({0});",
ids_str
),
Vec::new(), )
}
}
#[async_trait]
impl DatabaseInstruction for Remove<'_> {
type Output = Vec<Paper>;
async fn execute(&self, db: &mut Database) -> Result<Self::Output> {
let papers = self.query.execute(db).await?;
if !self.options.dry_run && !papers.is_empty() {
let papers_clone = papers.clone();
let ids: Vec<i64> = db
.conn
.call(move |conn| {
let mut ids = Vec::new();
let tx = conn.transaction()?;
for paper in &papers_clone {
let (sql, params) = Self::build_paper_ids_sql(paper);
if let Ok(id) = tx.query_row(&sql, params_from_iter(params), |row| row.get(0)) {
ids.push(id);
}
}
tx.commit()?;
Ok(ids)
})
.await?;
if !ids.is_empty() {
let (remove_sql, _) = Self::build_remove_sql(&ids);
db.conn
.call(move |conn| {
let tx = conn.transaction()?;
tx.execute_batch(&remove_sql)?;
tx.commit()?;
Ok(())
})
.await?;
}
}
Ok(papers)
}
}