use chrono::Utc;
use diesel::prelude::*;
#[cfg(feature = "napi")]
use napi_derive::napi;
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
#[cfg(feature = "pyo3")]
use rbox_derives::PyMutableMapping;
use uuid::Uuid;
use super::agent_registry::AgentRegistry;
use super::schema::{djmdAlbum, djmdArtist};
use super::{Date, DateString, RandomIdGenerator};
use crate::model_traits::{Model, ModelDelete, ModelInsert, ModelUpdate};
#[cfg(feature = "pyo3")]
use crate::util::{PyItemsIter, PyObjectIter, PyStrIter};
#[derive(Debug, Clone, PartialEq, Default, HasQuery, Identifiable, Insertable, AsChangeset)]
#[diesel(table_name = djmdArtist)]
#[diesel(primary_key(id))]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all, mapping))]
#[cfg_attr(feature = "pyo3", derive(PyMutableMapping))]
#[cfg_attr(feature = "napi", napi(object))]
pub struct DjmdArtist {
pub id: String,
pub uuid: String,
pub rb_data_status: i32,
pub rb_local_data_status: i32,
pub rb_local_deleted: i32,
pub rb_local_synced: i32,
pub usn: Option<i32>,
pub rb_local_usn: Option<i32>,
#[diesel(serialize_as = DateString)]
#[diesel(deserialize_as = DateString)]
pub created_at: Date,
#[diesel(serialize_as = DateString)]
#[diesel(deserialize_as = DateString)]
pub updated_at: Date,
pub name: String,
pub search_str: Option<String>,
}
impl Model for DjmdArtist {
type Id = str;
fn all(conn: &mut SqliteConnection) -> QueryResult<Vec<Self>> {
Self::query().load(conn)
}
fn find(conn: &mut SqliteConnection, id: &str) -> QueryResult<Option<Self>> {
Self::query().find(id).first(conn).optional()
}
fn id_exists(conn: &mut SqliteConnection, id: &str) -> QueryResult<bool> {
diesel::dsl::select(diesel::dsl::exists(Self::query().find(id))).get_result(conn)
}
}
impl ModelUpdate for DjmdArtist {
fn update(mut self, conn: &mut SqliteConnection) -> QueryResult<Self> {
let existing = match Self::find(conn, &self.id)? {
Some(e) => e,
None => return Err(diesel::result::Error::NotFound),
};
let mut changes = 0;
if self.name != existing.name {
changes += 1;
}
if self.search_str != existing.search_str {
changes += 1;
}
if changes == 0 {
return Ok(existing);
}
self.updated_at = Utc::now();
self.rb_local_usn = Some(AgentRegistry::increment_local_usn_by(conn, changes)?);
diesel::update(djmdArtist::table.find(self.id.clone()))
.set(self)
.get_result(conn)
}
}
impl ModelDelete for DjmdArtist {
fn delete(conn: &mut SqliteConnection, id: &str) -> QueryResult<usize> {
let result = diesel::delete(djmdArtist::table.find(id)).execute(conn)?;
AgentRegistry::increment_local_usn(conn)?;
diesel::sql_query(
r#"UPDATE djmdContent
SET ArtistID = CASE WHEN ArtistID =? THEN NULL ELSE ArtistID END,
RemixerID = CASE WHEN RemixerID =? THEN NULL ELSE RemixerID END,
OrgArtistID = CASE WHEN OrgArtistID =? THEN NULL ELSE OrgArtistID END,
ComposerID = CASE WHEN ComposerID =? THEN NULL ELSE ComposerID END,
Lyricist = CASE WHEN Lyricist =? THEN NULL ELSE Lyricist END
WHERE ArtistID =? OR RemixerID =? OR OrgArtistID =? OR ComposerID =? OR Lyricist =?;"#,
)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.bind::<diesel::sql_types::Text, _>(id)
.execute(conn)?;
diesel::update(djmdAlbum::table.filter(djmdAlbum::album_artist_id.eq(id)))
.set(djmdAlbum::album_artist_id.eq(None::<String>))
.execute(conn)?;
Ok(result)
}
fn delete_all(conn: &mut SqliteConnection, ids: Vec<&Self::Id>) -> QueryResult<usize> {
if ids.is_empty() {
return Ok(0);
}
let result =
diesel::delete(djmdArtist::table.filter(djmdArtist::id.eq_any(&ids))).execute(conn)?;
AgentRegistry::increment_local_usn_by(conn, result)?;
let ids_json = serde_json::to_string(&ids)
.map_err(|e| diesel::result::Error::DeserializationError(Box::new(e)))?;
diesel::sql_query(
r#"WITH ids(val) AS (SELECT value FROM json_each(?))
UPDATE djmdContent
SET ArtistID = CASE WHEN ArtistID IN (SELECT val FROM ids) THEN NULL ELSE ArtistID END,
RemixerID = CASE WHEN RemixerID IN (SELECT val FROM ids) THEN NULL ELSE RemixerID END,
OrgArtistID = CASE WHEN OrgArtistID IN (SELECT val FROM ids) THEN NULL ELSE OrgArtistID END,
ComposerID = CASE WHEN ComposerID IN (SELECT val FROM ids) THEN NULL ELSE ComposerID END,
Lyricist = CASE WHEN Lyricist IN (SELECT val FROM ids) THEN NULL ELSE Lyricist END
WHERE artist_id IN (SELECT val FROM ids)
OR RemixerID IN (SELECT val FROM ids)
OR OrgArtistID IN (SELECT val FROM ids)
OR ComposerID IN (SELECT val FROM ids)
OR Lyricist IN (SELECT val FROM ids);"#
)
.bind::<diesel::sql_types::Text, _>(ids_json)
.execute(conn)?;
diesel::update(djmdAlbum::table.filter(djmdAlbum::album_artist_id.eq_any(ids)))
.set(djmdAlbum::album_artist_id.eq(None::<String>))
.execute(conn)?;
Ok(result)
}
}
impl DjmdArtist {
pub fn find_by_name(conn: &mut SqliteConnection, name: &str) -> QueryResult<Option<Self>> {
Self::query()
.filter(djmdArtist::name.eq(name))
.first(conn)
.optional()
}
pub fn name_exists(conn: &mut SqliteConnection, name: &str) -> QueryResult<bool> {
let query = Self::query().filter(djmdArtist::name.eq(name));
diesel::dsl::select(diesel::dsl::exists(query)).get_result(conn)
}
fn generate_id(conn: &mut SqliteConnection) -> QueryResult<String> {
let generator = RandomIdGenerator::new(true);
let mut id: String = String::new();
for id_result in generator {
if let Ok(tmp_id) = id_result {
if !Self::id_exists(conn, &tmp_id)? {
id = tmp_id;
break;
}
}
}
Ok(id)
}
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all, mapping))]
#[cfg_attr(feature = "pyo3", derive(PyMutableMapping))]
#[cfg_attr(feature = "napi", napi(object))]
pub struct NewDjmdArtist {
pub name: String,
pub search_str: Option<String>,
}
impl ModelInsert for NewDjmdArtist {
type Model = DjmdArtist;
fn insert(self, conn: &mut SqliteConnection) -> QueryResult<Self::Model> {
let id = Self::Model::generate_id(conn)?;
let uuid = Uuid::new_v4().to_string();
let usn = AgentRegistry::increment_local_usn(conn)?;
let now = Utc::now();
let item = Self::Model {
id,
uuid,
rb_local_usn: Some(usn),
created_at: now,
updated_at: now,
name: self.name,
search_str: self.search_str,
..Default::default()
};
diesel::insert_into(djmdArtist::table)
.values(item)
.get_result(conn)
}
}
impl NewDjmdArtist {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
..Default::default()
}
}
pub fn insert_if_not_exists(self, conn: &mut SqliteConnection) -> QueryResult<DjmdArtist> {
match DjmdArtist::find_by_name(conn, &self.name)? {
Some(e) => Ok(e),
None => self.insert(conn),
}
}
pub fn search_str<S: Into<String>>(mut self, search_str: S) -> Self {
self.search_str = Some(search_str.into());
self
}
}