#![allow(unused_mut)] use diesel::prelude::*; use schema::{episode, podcast, source}; use models::queryables::{Episode, Podcast, Source}; use errors::*; use dbqueries; use diesel; use database::connection; trait Insert { fn insert(&self, &SqliteConnection) -> QueryResult; } trait Update { fn update(&self, &SqliteConnection, i32) -> QueryResult; } #[derive(Insertable)] #[table_name = "source"] #[derive(Debug, Clone, Default, Builder, PartialEq)] #[builder(default)] #[builder(derive(Debug))] #[builder(setter(into))] pub(crate) struct NewSource { uri: String, last_modified: Option, http_etag: Option, } impl Insert for NewSource { fn insert(&self, con: &SqliteConnection) -> QueryResult { use schema::source::dsl::*; diesel::insert_into(source).values(self).execute(&*con) } } impl NewSource { pub(crate) fn new(uri: &str) -> NewSource { NewSource { uri: uri.trim().to_string(), last_modified: None, http_etag: None, } } fn index(&self) -> Result<()> { let db = connection(); let con = db.get()?; // Throw away the result like `insert or ignore` // Diesel deos not support `insert or ignore` yet. let _ = self.insert(&con); Ok(()) } // Look out for when tryinto lands into stable. pub(crate) fn into_source(self) -> Result { self.index()?; dbqueries::get_source_from_uri(&self.uri) } } #[derive(Insertable, AsChangeset)] #[table_name = "podcast"] #[derive(Debug, Clone, Default, Builder, PartialEq)] #[builder(default)] #[builder(derive(Debug))] #[builder(setter(into))] pub(crate) struct NewPodcast { title: String, link: String, description: String, image_uri: Option, source_id: i32, } impl Insert for NewPodcast { fn insert(&self, con: &SqliteConnection) -> QueryResult { use schema::podcast::dsl::*; diesel::insert_into(podcast).values(self).execute(&*con) } } impl Update for NewPodcast { fn update(&self, con: &SqliteConnection, podcast_id: i32) -> QueryResult { use schema::podcast::dsl::*; info!("Updating {}", self.title); diesel::update(podcast.filter(id.eq(podcast_id))) .set(self) .execute(&*con) } } impl NewPodcast { // Look out for when tryinto lands into stable. pub(crate) fn into_podcast(self) -> Result { self.index()?; Ok(dbqueries::get_podcast_from_source_id(self.source_id)?) } pub(crate) fn index(&self) -> Result<()> { let pd = dbqueries::get_podcast_from_source_id(self.source_id); let db = connection(); let con = db.get()?; match pd { Ok(foo) => { if (foo.link() != self.link) || (foo.title() != self.title) || (foo.image_uri() != self.image_uri.as_ref().map(|x| x.as_str())) { info!("NewEpisode: {:?}\n OldEpisode: {:?}", self, foo); self.update(&con, foo.id())?; } } Err(_) => { self.insert(&con)?; } } Ok(()) } } #[allow(dead_code)] // Ignore the following geters. They are used in unit tests mainly. impl NewPodcast { pub(crate) fn source_id(&self) -> i32 { self.source_id } pub(crate) fn title(&self) -> &str { &self.title } pub(crate) fn link(&self) -> &str { &self.link } pub(crate) fn description(&self) -> &str { &self.description } pub(crate) fn image_uri(&self) -> Option<&str> { self.image_uri.as_ref().map(|s| s.as_str()) } } #[derive(Insertable, AsChangeset)] #[table_name = "episode"] #[derive(Debug, Clone, Default, Builder, PartialEq)] #[builder(default)] #[builder(derive(Debug))] #[builder(setter(into))] pub(crate) struct NewEpisode { title: String, uri: Option, description: Option, length: Option, duration: Option, guid: Option, epoch: i32, podcast_id: i32, } impl Insert for NewEpisode { fn insert(&self, con: &SqliteConnection) -> QueryResult { use schema::episode::dsl::*; diesel::insert_into(episode).values(self).execute(&*con) } } impl Update for NewEpisode { fn update(&self, con: &SqliteConnection, episode_id: i32) -> QueryResult { use schema::episode::dsl::*; info!("Updating {:?}", self.title); diesel::update(episode.filter(rowid.eq(episode_id))) .set(self) .execute(&*con) } } impl NewEpisode { // TODO: Refactor into batch indexes instead. #[allow(dead_code)] pub(crate) fn into_episode(self, con: &SqliteConnection) -> Result { self.index(con)?; Ok(dbqueries::get_episode_from_pk( con, &self.title, self.podcast_id, )?) } pub(crate) fn index(&self, con: &SqliteConnection) -> QueryResult<()> { let ep = dbqueries::get_episode_from_pk(con, &self.title, self.podcast_id); match ep { Ok(foo) => { if foo.podcast_id() != self.podcast_id { error!("NEP pid: {}, EP pid: {}", self.podcast_id, foo.podcast_id()); }; if foo.title() != self.title.as_str() || foo.epoch() != self.epoch || foo.uri() != self.uri.as_ref().map(|s| s.as_str()) || foo.duration() != self.duration { self.update(con, foo.rowid())?; } } Err(_) => { self.insert(con)?; } } Ok(()) } } #[allow(dead_code)] // Ignore the following getters. They are used in unit tests mainly. impl NewEpisode { pub(crate) fn title(&self) -> &str { self.title.as_ref() } pub(crate) fn uri(&self) -> Option<&str> { self.uri.as_ref().map(|s| s.as_str()) } pub(crate) fn description(&self) -> Option<&str> { self.description.as_ref().map(|s| s.as_str()) } pub(crate) fn guid(&self) -> Option<&str> { self.guid.as_ref().map(|s| s.as_str()) } pub(crate) fn epoch(&self) -> i32 { self.epoch } pub(crate) fn length(&self) -> Option { self.length } pub(crate) fn podcast_id(&self) -> i32 { self.podcast_id } }