Migrate to diesel 2.0 (#174)

This commit is contained in:
Tobias Schmitz 2022-08-30 20:47:16 +02:00 committed by GitHub
parent 41a4b21327
commit f5a2eed423
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 466 additions and 494 deletions

694
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -21,13 +21,13 @@ base64 = "0.13"
branca = "0.10.1" branca = "0.10.1"
cookie = { version = "0.16", features = ["signed", "key-expansion"] } cookie = { version = "0.16", features = ["signed", "key-expansion"] }
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
diesel_migrations = { version = "1.4", features = ["sqlite"] } diesel_migrations = { version = "2.0", features = ["sqlite"] }
futures-util = { version = "0.3" } futures-util = { version = "0.3" }
getopts = "0.2.21" getopts = "0.2.21"
http = "0.2.6" http = "0.2.6"
id3 = "1.0.2" id3 = "1.0.2"
lewton = "0.10.2" lewton = "0.10.2"
libsqlite3-sys = { version = "0.22", features = ["bundled", "bundled-windows"], optional = true } libsqlite3-sys = { version = "0.25", features = ["bundled", "bundled-windows"], optional = true }
log = "0.4.14" log = "0.4.14"
metaflac = "0.2.5" metaflac = "0.2.5"
mp3-duration = "0.1.10" mp3-duration = "0.1.10"
@ -51,7 +51,7 @@ ureq = "1.5.5"
url = "2.2" url = "2.2"
[dependencies.diesel] [dependencies.diesel]
version = "1.4.8" version = "2.0.0"
default_features = false default_features = false
features = ["libsqlite3-sys", "r2d2", "sqlite", "64-column-tables"] features = ["libsqlite3-sys", "r2d2", "sqlite", "64-column-tables"]

View file

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::db::ddns_config; use crate::db::ddns_config;
#[derive(Clone, Debug, Deserialize, Insertable, PartialEq, Eq, Queryable, Serialize)] #[derive(Clone, Debug, Deserialize, Insertable, PartialEq, Eq, Queryable, Serialize)]
#[table_name = "ddns_config"] #[diesel(table_name = ddns_config)]
pub struct Config { pub struct Config {
pub host: String, pub host: String,
pub username: String, pub username: String,

View file

@ -43,22 +43,22 @@ impl Manager {
pub fn config(&self) -> Result<Config> { pub fn config(&self) -> Result<Config> {
use crate::db::ddns_config::dsl::*; use crate::db::ddns_config::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
Ok(ddns_config Ok(ddns_config
.select((host, username, password)) .select((host, username, password))
.get_result(&connection)?) .get_result(&mut connection)?)
} }
pub fn set_config(&self, new_config: &Config) -> Result<()> { pub fn set_config(&self, new_config: &Config) -> Result<()> {
use crate::db::ddns_config::dsl::*; use crate::db::ddns_config::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
diesel::update(ddns_config) diesel::update(ddns_config)
.set(( .set((
host.eq(&new_config.host), host.eq(&new_config.host),
username.eq(&new_config.username), username.eq(&new_config.username),
password.eq(&new_config.password), password.eq(&new_config.password),
)) ))
.execute(&connection)?; .execute(&mut connection)?;
Ok(()) Ok(())
} }

View file

@ -21,10 +21,9 @@ impl From<anyhow::Error> for QueryError {
} }
} }
no_arg_sql_function!( sql_function!(
random, #[aggregate]
sql_types::Integer, fn random() -> Integer;
"Represents the SQL RANDOM() function"
); );
impl Index { impl Index {
@ -34,13 +33,13 @@ impl Index {
{ {
let mut output = Vec::new(); let mut output = Vec::new();
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
if virtual_path.as_ref().components().count() == 0 { if virtual_path.as_ref().components().count() == 0 {
// Browse top-level // Browse top-level
let real_directories: Vec<Directory> = directories::table let real_directories: Vec<Directory> = directories::table
.filter(directories::parent.is_null()) .filter(directories::parent.is_null())
.load(&connection) .load(&mut connection)
.map_err(anyhow::Error::new)?; .map_err(anyhow::Error::new)?;
let virtual_directories = real_directories let virtual_directories = real_directories
.into_iter() .into_iter()
@ -56,7 +55,7 @@ impl Index {
let real_directories: Vec<Directory> = directories::table let real_directories: Vec<Directory> = directories::table
.filter(directories::parent.eq(&real_path_string)) .filter(directories::parent.eq(&real_path_string))
.order(sql::<sql_types::Bool>("path COLLATE NOCASE ASC")) .order(sql::<sql_types::Bool>("path COLLATE NOCASE ASC"))
.load(&connection) .load(&mut connection)
.map_err(anyhow::Error::new)?; .map_err(anyhow::Error::new)?;
let virtual_directories = real_directories let virtual_directories = real_directories
.into_iter() .into_iter()
@ -66,7 +65,7 @@ impl Index {
let real_songs: Vec<Song> = songs::table let real_songs: Vec<Song> = songs::table
.filter(songs::parent.eq(&real_path_string)) .filter(songs::parent.eq(&real_path_string))
.order(sql::<sql_types::Bool>("path COLLATE NOCASE ASC")) .order(sql::<sql_types::Bool>("path COLLATE NOCASE ASC"))
.load(&connection) .load(&mut connection)
.map_err(anyhow::Error::new)?; .map_err(anyhow::Error::new)?;
let virtual_songs = real_songs.into_iter().filter_map(|s| s.virtualize(&vfs)); let virtual_songs = real_songs.into_iter().filter_map(|s| s.virtualize(&vfs));
output.extend(virtual_songs.map(CollectionFile::Song)); output.extend(virtual_songs.map(CollectionFile::Song));
@ -81,7 +80,7 @@ impl Index {
{ {
use self::songs::dsl::*; use self::songs::dsl::*;
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let real_songs: Vec<Song> = if virtual_path.as_ref().parent() != None { let real_songs: Vec<Song> = if virtual_path.as_ref().parent() != None {
let real_path = vfs let real_path = vfs
@ -95,12 +94,12 @@ impl Index {
songs songs
.filter(path.like(&song_path_filter)) .filter(path.like(&song_path_filter))
.order(path) .order(path)
.load(&connection) .load(&mut connection)
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
} else { } else {
songs songs
.order(path) .order(path)
.load(&connection) .load(&mut connection)
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
}; };
@ -111,12 +110,12 @@ impl Index {
pub fn get_random_albums(&self, count: i64) -> anyhow::Result<Vec<Directory>> { pub fn get_random_albums(&self, count: i64) -> anyhow::Result<Vec<Directory>> {
use self::directories::dsl::*; use self::directories::dsl::*;
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let real_directories: Vec<Directory> = directories let real_directories: Vec<Directory> = directories
.filter(album.is_not_null()) .filter(album.is_not_null())
.limit(count) .limit(count)
.order(random) .order(random())
.load(&connection)?; .load(&mut connection)?;
let virtual_directories = real_directories let virtual_directories = real_directories
.into_iter() .into_iter()
.filter_map(|d| d.virtualize(&vfs)); .filter_map(|d| d.virtualize(&vfs));
@ -126,12 +125,12 @@ impl Index {
pub fn get_recent_albums(&self, count: i64) -> anyhow::Result<Vec<Directory>> { pub fn get_recent_albums(&self, count: i64) -> anyhow::Result<Vec<Directory>> {
use self::directories::dsl::*; use self::directories::dsl::*;
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let real_directories: Vec<Directory> = directories let real_directories: Vec<Directory> = directories
.filter(album.is_not_null()) .filter(album.is_not_null())
.order(date_added.desc()) .order(date_added.desc())
.limit(count) .limit(count)
.load(&connection)?; .load(&mut connection)?;
let virtual_directories = real_directories let virtual_directories = real_directories
.into_iter() .into_iter()
.filter_map(|d| d.virtualize(&vfs)); .filter_map(|d| d.virtualize(&vfs));
@ -140,7 +139,7 @@ impl Index {
pub fn search(&self, query: &str) -> anyhow::Result<Vec<CollectionFile>> { pub fn search(&self, query: &str) -> anyhow::Result<Vec<CollectionFile>> {
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let like_test = format!("%{}%", query); let like_test = format!("%{}%", query);
let mut output = Vec::new(); let mut output = Vec::new();
@ -150,7 +149,7 @@ impl Index {
let real_directories: Vec<Directory> = directories let real_directories: Vec<Directory> = directories
.filter(path.like(&like_test)) .filter(path.like(&like_test))
.filter(parent.not_like(&like_test)) .filter(parent.not_like(&like_test))
.load(&connection)?; .load(&mut connection)?;
let virtual_directories = real_directories let virtual_directories = real_directories
.into_iter() .into_iter()
@ -171,7 +170,7 @@ impl Index {
.or(album_artist.like(&like_test)), .or(album_artist.like(&like_test)),
) )
.filter(parent.not_like(&like_test)) .filter(parent.not_like(&like_test))
.load(&connection)?; .load(&mut connection)?;
let virtual_songs = real_songs.into_iter().filter_map(|d| d.virtualize(&vfs)); let virtual_songs = real_songs.into_iter().filter_map(|d| d.virtualize(&vfs));
@ -183,7 +182,7 @@ impl Index {
pub fn get_song(&self, virtual_path: &Path) -> anyhow::Result<Song> { pub fn get_song(&self, virtual_path: &Path) -> anyhow::Result<Song> {
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let real_path = vfs.virtual_to_real(virtual_path)?; let real_path = vfs.virtual_to_real(virtual_path)?;
let real_path_string = real_path.as_path().to_string_lossy(); let real_path_string = real_path.as_path().to_string_lossy();
@ -191,7 +190,7 @@ impl Index {
use self::songs::dsl::*; use self::songs::dsl::*;
let real_song: Song = songs let real_song: Song = songs
.filter(path.eq(real_path_string)) .filter(path.eq(real_path_string))
.get_result(&connection)?; .get_result(&mut connection)?;
match real_song.virtualize(&vfs) { match real_song.virtualize(&vfs) {
Some(s) => Ok(s), Some(s) => Ok(s),

View file

@ -18,9 +18,9 @@ fn update_adds_new_content() {
ctx.index.update().unwrap(); ctx.index.update().unwrap();
ctx.index.update().unwrap(); // Validates that subsequent updates don't run into conflicts ctx.index.update().unwrap(); // Validates that subsequent updates don't run into conflicts
let connection = ctx.db.connect().unwrap(); let mut connection = ctx.db.connect().unwrap();
let all_directories: Vec<Directory> = directories::table.load(&connection).unwrap(); let all_directories: Vec<Directory> = directories::table.load(&mut connection).unwrap();
let all_songs: Vec<Song> = songs::table.load(&connection).unwrap(); let all_songs: Vec<Song> = songs::table.load(&mut connection).unwrap();
assert_eq!(all_directories.len(), 6); assert_eq!(all_directories.len(), 6);
assert_eq!(all_songs.len(), 13); assert_eq!(all_songs.len(), 13);
} }
@ -47,9 +47,9 @@ fn update_removes_missing_content() {
ctx.index.update().unwrap(); ctx.index.update().unwrap();
{ {
let connection = ctx.db.connect().unwrap(); let mut connection = ctx.db.connect().unwrap();
let all_directories: Vec<Directory> = directories::table.load(&connection).unwrap(); let all_directories: Vec<Directory> = directories::table.load(&mut connection).unwrap();
let all_songs: Vec<Song> = songs::table.load(&connection).unwrap(); let all_songs: Vec<Song> = songs::table.load(&mut connection).unwrap();
assert_eq!(all_directories.len(), 6); assert_eq!(all_directories.len(), 6);
assert_eq!(all_songs.len(), 13); assert_eq!(all_songs.len(), 13);
} }
@ -58,9 +58,9 @@ fn update_removes_missing_content() {
std::fs::remove_dir_all(&khemmis_directory).unwrap(); std::fs::remove_dir_all(&khemmis_directory).unwrap();
ctx.index.update().unwrap(); ctx.index.update().unwrap();
{ {
let connection = ctx.db.connect().unwrap(); let mut connection = ctx.db.connect().unwrap();
let all_directories: Vec<Directory> = directories::table.load(&connection).unwrap(); let all_directories: Vec<Directory> = directories::table.load(&mut connection).unwrap();
let all_songs: Vec<Song> = songs::table.load(&connection).unwrap(); let all_songs: Vec<Song> = songs::table.load(&mut connection).unwrap();
assert_eq!(all_directories.len(), 4); assert_eq!(all_directories.len(), 4);
assert_eq!(all_songs.len(), 8); assert_eq!(all_songs.len(), 8);
} }

View file

@ -11,7 +11,7 @@ pub enum CollectionFile {
} }
#[derive(Debug, PartialEq, Eq, Queryable, QueryableByName, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Queryable, QueryableByName, Serialize, Deserialize)]
#[table_name = "songs"] #[diesel(table_name = songs)]
pub struct Song { pub struct Song {
#[serde(skip_serializing, skip_deserializing)] #[serde(skip_serializing, skip_deserializing)]
id: i32, id: i32,

View file

@ -22,15 +22,15 @@ impl Cleaner {
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
let all_directories: Vec<String> = { let all_directories: Vec<String> = {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
directories::table directories::table
.select(directories::path) .select(directories::path)
.load(&connection)? .load(&mut connection)?
}; };
let all_songs: Vec<String> = { let all_songs: Vec<String> = {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
songs::table.select(songs::path).load(&connection)? songs::table.select(songs::path).load(&mut connection)?
}; };
let list_missing_directories = || { let list_missing_directories = || {
@ -58,14 +58,14 @@ impl Cleaner {
thread_pool.join(list_missing_directories, list_missing_songs); thread_pool.join(list_missing_directories, list_missing_songs);
{ {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) { for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
diesel::delete(directories::table.filter(directories::path.eq_any(chunk))) diesel::delete(directories::table.filter(directories::path.eq_any(chunk)))
.execute(&connection)?; .execute(&mut connection)?;
} }
for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) { for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
diesel::delete(songs::table.filter(songs::path.eq_any(chunk))) diesel::delete(songs::table.filter(songs::path.eq_any(chunk)))
.execute(&connection)?; .execute(&mut connection)?;
} }
} }

View file

@ -8,7 +8,7 @@ use crate::db::{directories, songs, DB};
const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction
#[derive(Debug, Insertable)] #[derive(Debug, Insertable)]
#[table_name = "songs"] #[diesel(table_name = songs)]
pub struct Song { pub struct Song {
pub path: String, pub path: String,
pub parent: String, pub parent: String,
@ -28,7 +28,7 @@ pub struct Song {
} }
#[derive(Debug, Insertable)] #[derive(Debug, Insertable)]
#[table_name = "directories"] #[diesel(table_name = directories)]
pub struct Directory { pub struct Directory {
pub path: String, pub path: String,
pub parent: Option<String>, pub parent: Option<String>,
@ -87,10 +87,10 @@ impl Inserter {
} }
fn flush_directories(&mut self) { fn flush_directories(&mut self) {
let res = self.db.connect().and_then(|connection| { let res = self.db.connect().and_then(|mut connection| {
diesel::insert_into(directories::table) diesel::insert_into(directories::table)
.values(&self.new_directories) .values(&self.new_directories)
.execute(&*connection) // TODO https://github.com/diesel-rs/diesel/issues/1822 .execute(&mut *connection) // TODO https://github.com/diesel-rs/diesel/issues/1822
.map_err(Error::new) .map_err(Error::new)
}); });
if res.is_err() { if res.is_err() {
@ -100,10 +100,10 @@ impl Inserter {
} }
fn flush_songs(&mut self) { fn flush_songs(&mut self) {
let res = self.db.connect().and_then(|connection| { let res = self.db.connect().and_then(|mut connection| {
diesel::insert_into(songs::table) diesel::insert_into(songs::table)
.values(&self.new_songs) .values(&self.new_songs)
.execute(&*connection) // TODO https://github.com/diesel-rs/diesel/issues/1822 .execute(&mut *connection) // TODO https://github.com/diesel-rs/diesel/issues/1822
.map_err(Error::new) .map_err(Error::new)
}); });
if res.is_err() { if res.is_err() {

View file

@ -22,14 +22,14 @@ impl Manager {
} }
pub fn list_playlists(&self, owner: &str) -> Result<Vec<String>, Error> { pub fn list_playlists(&self, owner: &str) -> Result<Vec<String>, Error> {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let user: User = { let user: User = {
use self::users::dsl::*; use self::users::dsl::*;
users users
.filter(name.eq(owner)) .filter(name.eq(owner))
.select((id,)) .select((id,))
.first(&connection) .first(&mut connection)
.optional() .optional()
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
.ok_or(Error::UserNotFound)? .ok_or(Error::UserNotFound)?
@ -39,7 +39,7 @@ impl Manager {
use self::playlists::dsl::*; use self::playlists::dsl::*;
let found_playlists: Vec<String> = Playlist::belonging_to(&user) let found_playlists: Vec<String> = Playlist::belonging_to(&user)
.select(name) .select(name)
.load(&connection) .load(&mut connection)
.map_err(anyhow::Error::new)?; .map_err(anyhow::Error::new)?;
Ok(found_playlists) Ok(found_playlists)
} }
@ -56,7 +56,7 @@ impl Manager {
let vfs = self.vfs_manager.get_vfs()?; let vfs = self.vfs_manager.get_vfs()?;
{ {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
// Find owner // Find owner
let user: User = { let user: User = {
@ -64,7 +64,7 @@ impl Manager {
users users
.filter(name.eq(owner)) .filter(name.eq(owner))
.select((id,)) .select((id,))
.first(&connection) .first(&mut connection)
.optional() .optional()
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
.ok_or(Error::UserNotFound)? .ok_or(Error::UserNotFound)?
@ -78,7 +78,7 @@ impl Manager {
diesel::insert_into(playlists::table) diesel::insert_into(playlists::table)
.values(&new_playlist) .values(&new_playlist)
.execute(&connection) .execute(&mut connection)
.map_err(anyhow::Error::new)?; .map_err(anyhow::Error::new)?;
playlist = { playlist = {
@ -86,7 +86,7 @@ impl Manager {
playlists playlists
.select((id, owner)) .select((id, owner))
.filter(name.eq(playlist_name).and(owner.eq(user.id))) .filter(name.eq(playlist_name).and(owner.eq(user.id)))
.get_result(&connection) .get_result(&mut connection)
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
} }
} }
@ -110,17 +110,17 @@ impl Manager {
} }
{ {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
connection connection
.transaction::<_, diesel::result::Error, _>(|| { .transaction::<_, diesel::result::Error, _>(|connection| {
// Delete old content (if any) // Delete old content (if any)
let old_songs = PlaylistSong::belonging_to(&playlist); let old_songs = PlaylistSong::belonging_to(&playlist);
diesel::delete(old_songs).execute(&connection)?; diesel::delete(old_songs).execute(connection)?;
// Insert content // Insert content
diesel::insert_into(playlist_songs::table) diesel::insert_into(playlist_songs::table)
.values(&new_songs) .values(&new_songs)
.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822 .execute(&mut *connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
Ok(()) Ok(())
}) })
.map_err(anyhow::Error::new)?; .map_err(anyhow::Error::new)?;
@ -134,7 +134,7 @@ impl Manager {
let songs: Vec<Song>; let songs: Vec<Song>;
{ {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
// Find owner // Find owner
let user: User = { let user: User = {
@ -142,7 +142,7 @@ impl Manager {
users users
.filter(name.eq(owner)) .filter(name.eq(owner))
.select((id,)) .select((id,))
.first(&connection) .first(&mut connection)
.optional() .optional()
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
.ok_or(Error::UserNotFound)? .ok_or(Error::UserNotFound)?
@ -154,7 +154,7 @@ impl Manager {
playlists playlists
.select((id, owner)) .select((id, owner))
.filter(name.eq(playlist_name).and(owner.eq(user.id))) .filter(name.eq(playlist_name).and(owner.eq(user.id)))
.get_result(&connection) .get_result(&mut connection)
.optional() .optional()
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
.ok_or(Error::PlaylistNotFound)? .ok_or(Error::PlaylistNotFound)?
@ -171,7 +171,9 @@ impl Manager {
"#, "#,
); );
let query = query.bind::<sql_types::Integer, _>(playlist.id); let query = query.bind::<sql_types::Integer, _>(playlist.id);
songs = query.get_results(&connection).map_err(anyhow::Error::new)?; songs = query
.get_results(&mut connection)
.map_err(anyhow::Error::new)?;
} }
// Map real path to virtual paths // Map real path to virtual paths
@ -184,14 +186,14 @@ impl Manager {
} }
pub fn delete_playlist(&self, playlist_name: &str, owner: &str) -> Result<(), Error> { pub fn delete_playlist(&self, playlist_name: &str, owner: &str) -> Result<(), Error> {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let user: User = { let user: User = {
use self::users::dsl::*; use self::users::dsl::*;
users users
.filter(name.eq(owner)) .filter(name.eq(owner))
.select((id,)) .select((id,))
.first(&connection) .first(&mut connection)
.optional() .optional()
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
.ok_or(Error::UserNotFound)? .ok_or(Error::UserNotFound)?
@ -201,7 +203,7 @@ impl Manager {
use self::playlists::dsl::*; use self::playlists::dsl::*;
let q = Playlist::belonging_to(&user).filter(name.eq(playlist_name)); let q = Playlist::belonging_to(&user).filter(name.eq(playlist_name));
match diesel::delete(q) match diesel::delete(q)
.execute(&connection) .execute(&mut connection)
.map_err(anyhow::Error::new)? .map_err(anyhow::Error::new)?
{ {
0 => Err(Error::PlaylistNotFound), 0 => Err(Error::PlaylistNotFound),
@ -212,28 +214,28 @@ impl Manager {
} }
#[derive(Identifiable, Queryable, Associations)] #[derive(Identifiable, Queryable, Associations)]
#[belongs_to(User, foreign_key = "owner")] #[diesel(belongs_to(User, foreign_key = owner))]
struct Playlist { struct Playlist {
id: i32, id: i32,
owner: i32, owner: i32,
} }
#[derive(Identifiable, Queryable, Associations)] #[derive(Identifiable, Queryable, Associations)]
#[belongs_to(Playlist, foreign_key = "playlist")] #[diesel(belongs_to(Playlist, foreign_key = playlist))]
struct PlaylistSong { struct PlaylistSong {
id: i32, id: i32,
playlist: i32, playlist: i32,
} }
#[derive(Insertable)] #[derive(Insertable)]
#[table_name = "playlists"] #[diesel(table_name = playlists)]
struct NewPlaylist { struct NewPlaylist {
name: String, name: String,
owner: i32, owner: i32,
} }
#[derive(Insertable)] #[derive(Insertable)]
#[table_name = "playlist_songs"] #[diesel(table_name = playlist_songs)]
struct NewPlaylistSong { struct NewPlaylistSong {
playlist: i32, playlist: i32,
path: String, path: String,

View file

@ -18,10 +18,10 @@ impl Manager {
pub fn get_auth_secret(&self) -> Result<AuthSecret, Error> { pub fn get_auth_secret(&self) -> Result<AuthSecret, Error> {
use self::misc_settings::dsl::*; use self::misc_settings::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let secret: Vec<u8> = misc_settings let secret: Vec<u8> = misc_settings
.select(auth_secret) .select(auth_secret)
.get_result(&connection) .get_result(&mut connection)
.map_err(|e| match e { .map_err(|e| match e {
diesel::result::Error::NotFound => Error::AuthSecretNotFound, diesel::result::Error::NotFound => Error::AuthSecretNotFound,
_ => Error::Unspecified, _ => Error::Unspecified,
@ -34,10 +34,10 @@ impl Manager {
pub fn get_index_sleep_duration(&self) -> Result<Duration, Error> { pub fn get_index_sleep_duration(&self) -> Result<Duration, Error> {
use self::misc_settings::dsl::*; use self::misc_settings::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
misc_settings misc_settings
.select(index_sleep_duration_seconds) .select(index_sleep_duration_seconds)
.get_result(&connection) .get_result(&mut connection)
.map_err(|e| match e { .map_err(|e| match e {
diesel::result::Error::NotFound => Error::IndexSleepDurationNotFound, diesel::result::Error::NotFound => Error::IndexSleepDurationNotFound,
_ => Error::Unspecified, _ => Error::Unspecified,
@ -47,10 +47,10 @@ impl Manager {
pub fn get_index_album_art_pattern(&self) -> Result<Regex, Error> { pub fn get_index_album_art_pattern(&self) -> Result<Regex, Error> {
use self::misc_settings::dsl::*; use self::misc_settings::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
misc_settings misc_settings
.select(index_album_art_pattern) .select(index_album_art_pattern)
.get_result(&connection) .get_result(&mut connection)
.map_err(|e| match e { .map_err(|e| match e {
diesel::result::Error::NotFound => Error::IndexAlbumArtPatternNotFound, diesel::result::Error::NotFound => Error::IndexAlbumArtPatternNotFound,
_ => Error::Unspecified, _ => Error::Unspecified,
@ -61,10 +61,10 @@ impl Manager {
} }
pub fn read(&self) -> Result<Settings, Error> { pub fn read(&self) -> Result<Settings, Error> {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let misc: MiscSettings = misc_settings::table let misc: MiscSettings = misc_settings::table
.get_result(&connection) .get_result(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(Settings { Ok(Settings {
@ -75,19 +75,19 @@ impl Manager {
} }
pub fn amend(&self, new_settings: &NewSettings) -> Result<(), Error> { pub fn amend(&self, new_settings: &NewSettings) -> Result<(), Error> {
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
if let Some(sleep_duration) = new_settings.reindex_every_n_seconds { if let Some(sleep_duration) = new_settings.reindex_every_n_seconds {
diesel::update(misc_settings::table) diesel::update(misc_settings::table)
.set(misc_settings::index_sleep_duration_seconds.eq(sleep_duration as i32)) .set(misc_settings::index_sleep_duration_seconds.eq(sleep_duration as i32))
.execute(&connection) .execute(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
} }
if let Some(ref album_art_pattern) = new_settings.album_art_pattern { if let Some(ref album_art_pattern) = new_settings.album_art_pattern {
diesel::update(misc_settings::table) diesel::update(misc_settings::table)
.set(misc_settings::index_album_art_pattern.eq(album_art_pattern)) .set(misc_settings::index_album_art_pattern.eq(album_art_pattern))
.execute(&connection) .execute(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
} }

View file

@ -27,7 +27,7 @@ impl Manager {
} }
let password_hash = hash_password(&new_user.password)?; let password_hash = hash_password(&new_user.password)?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let new_user = User { let new_user = User {
name: new_user.name.to_owned(), name: new_user.name.to_owned(),
password_hash, password_hash,
@ -36,48 +36,48 @@ impl Manager {
diesel::insert_into(users::table) diesel::insert_into(users::table)
.values(&new_user) .values(&new_user)
.execute(&connection) .execute(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(()) Ok(())
} }
pub fn delete(&self, username: &str) -> Result<(), Error> { pub fn delete(&self, username: &str) -> Result<(), Error> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
diesel::delete(users.filter(name.eq(username))) diesel::delete(users.filter(name.eq(username)))
.execute(&connection) .execute(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(()) Ok(())
} }
pub fn set_password(&self, username: &str, password: &str) -> Result<(), Error> { pub fn set_password(&self, username: &str, password: &str) -> Result<(), Error> {
let hash = hash_password(password)?; let hash = hash_password(password)?;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
diesel::update(users.filter(name.eq(username))) diesel::update(users.filter(name.eq(username)))
.set(password_hash.eq(hash)) .set(password_hash.eq(hash))
.execute(&connection) .execute(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(()) Ok(())
} }
pub fn set_is_admin(&self, username: &str, is_admin: bool) -> Result<(), Error> { pub fn set_is_admin(&self, username: &str, is_admin: bool) -> Result<(), Error> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
diesel::update(users.filter(name.eq(username))) diesel::update(users.filter(name.eq(username)))
.set(admin.eq(is_admin as i32)) .set(admin.eq(is_admin as i32))
.execute(&connection) .execute(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(()) Ok(())
} }
pub fn login(&self, username: &str, password: &str) -> Result<AuthToken, Error> { pub fn login(&self, username: &str, password: &str) -> Result<AuthToken, Error> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
match users match users
.select(password_hash) .select(password_hash)
.filter(name.eq(username)) .filter(name.eq(username))
.get_result(&connection) .get_result(&mut connection)
{ {
Err(diesel::result::Error::NotFound) => Err(Error::IncorrectUsername), Err(diesel::result::Error::NotFound) => Err(Error::IncorrectUsername),
Ok(hash) => { Ok(hash) => {
@ -146,38 +146,38 @@ impl Manager {
pub fn count(&self) -> anyhow::Result<i64> { pub fn count(&self) -> anyhow::Result<i64> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let count = users.count().get_result(&connection)?; let count = users.count().get_result(&mut connection)?;
Ok(count) Ok(count)
} }
pub fn list(&self) -> Result<Vec<User>, Error> { pub fn list(&self) -> Result<Vec<User>, Error> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
users users
.select((name, password_hash, admin)) .select((name, password_hash, admin))
.get_results(&connection) .get_results(&mut connection)
.map_err(|_| Error::Unspecified) .map_err(|_| Error::Unspecified)
} }
pub fn exists(&self, username: &str) -> Result<bool, Error> { pub fn exists(&self, username: &str) -> Result<bool, Error> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let results: Vec<String> = users let results: Vec<String> = users
.select(name) .select(name)
.filter(name.eq(username)) .filter(name.eq(username))
.get_results(&connection) .get_results(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(!results.is_empty()) Ok(!results.is_empty())
} }
pub fn is_admin(&self, username: &str) -> Result<bool, Error> { pub fn is_admin(&self, username: &str) -> Result<bool, Error> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let is_admin: i32 = users let is_admin: i32 = users
.filter(name.eq(username)) .filter(name.eq(username))
.select(admin) .select(admin)
.get_result(&connection) .get_result(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(is_admin != 0) Ok(is_admin != 0)
} }
@ -189,13 +189,13 @@ impl Manager {
session_key: &str, session_key: &str,
) -> Result<(), Error> { ) -> Result<(), Error> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
diesel::update(users.filter(name.eq(username))) diesel::update(users.filter(name.eq(username)))
.set(( .set((
lastfm_username.eq(lastfm_login), lastfm_username.eq(lastfm_login),
lastfm_session_key.eq(session_key), lastfm_session_key.eq(session_key),
)) ))
.execute(&connection) .execute(&mut connection)
.map_err(|_| Error::Unspecified)?; .map_err(|_| Error::Unspecified)?;
Ok(()) Ok(())
} }
@ -209,11 +209,11 @@ impl Manager {
pub fn get_lastfm_session_key(&self, username: &str) -> anyhow::Result<String> { pub fn get_lastfm_session_key(&self, username: &str) -> anyhow::Result<String> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let token = users let token = users
.filter(name.eq(username)) .filter(name.eq(username))
.select(lastfm_session_key) .select(lastfm_session_key)
.get_result(&connection)?; .get_result(&mut connection)?;
match token { match token {
Some(t) => Ok(t), Some(t) => Ok(t),
_ => Err(anyhow!("Missing LastFM credentials")), _ => Err(anyhow!("Missing LastFM credentials")),
@ -226,11 +226,11 @@ impl Manager {
pub fn lastfm_unlink(&self, username: &str) -> anyhow::Result<()> { pub fn lastfm_unlink(&self, username: &str) -> anyhow::Result<()> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let null: Option<String> = None; let null: Option<String> = None;
diesel::update(users.filter(name.eq(username))) diesel::update(users.filter(name.eq(username)))
.set((lastfm_session_key.eq(&null), lastfm_username.eq(&null))) .set((lastfm_session_key.eq(&null), lastfm_username.eq(&null)))
.execute(&connection)?; .execute(&mut connection)?;
Ok(()) Ok(())
} }
} }

View file

@ -13,7 +13,7 @@ pub use manager::*;
pub use preferences::*; pub use preferences::*;
#[derive(Debug, Insertable, Queryable)] #[derive(Debug, Insertable, Queryable)]
#[table_name = "users"] #[diesel(table_name = users)]
pub struct User { pub struct User {
pub name: String, pub name: String,
pub password_hash: String, pub password_hash: String,

View file

@ -14,11 +14,11 @@ pub struct Preferences {
impl Manager { impl Manager {
pub fn read_preferences(&self, username: &str) -> Result<Preferences> { pub fn read_preferences(&self, username: &str) -> Result<Preferences> {
use self::users::dsl::*; use self::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let (theme_base, theme_accent, read_lastfm_username) = users let (theme_base, theme_accent, read_lastfm_username) = users
.select((web_theme_base, web_theme_accent, lastfm_username)) .select((web_theme_base, web_theme_accent, lastfm_username))
.filter(name.eq(username)) .filter(name.eq(username))
.get_result(&connection)?; .get_result(&mut connection)?;
Ok(Preferences { Ok(Preferences {
web_theme_base: theme_base, web_theme_base: theme_base,
web_theme_accent: theme_accent, web_theme_accent: theme_accent,
@ -28,13 +28,13 @@ impl Manager {
pub fn write_preferences(&self, username: &str, preferences: &Preferences) -> Result<()> { pub fn write_preferences(&self, username: &str, preferences: &Preferences) -> Result<()> {
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
diesel::update(users.filter(name.eq(username))) diesel::update(users.filter(name.eq(username)))
.set(( .set((
web_theme_base.eq(&preferences.web_theme_base), web_theme_base.eq(&preferences.web_theme_base),
web_theme_accent.eq(&preferences.web_theme_accent), web_theme_accent.eq(&preferences.web_theme_accent),
)) ))
.execute(&connection)?; .execute(&mut connection)?;
Ok(()) Ok(())
} }
} }

View file

@ -23,20 +23,20 @@ impl Manager {
pub fn mount_dirs(&self) -> Result<Vec<MountDir>> { pub fn mount_dirs(&self) -> Result<Vec<MountDir>> {
use self::mount_points::dsl::*; use self::mount_points::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
let mount_dirs: Vec<MountDir> = mount_points let mount_dirs: Vec<MountDir> = mount_points
.select((source, name)) .select((source, name))
.get_results(&connection)?; .get_results(&mut connection)?;
Ok(mount_dirs) Ok(mount_dirs)
} }
pub fn set_mount_dirs(&self, mount_dirs: &[MountDir]) -> Result<()> { pub fn set_mount_dirs(&self, mount_dirs: &[MountDir]) -> Result<()> {
use self::mount_points::dsl::*; use self::mount_points::dsl::*;
let connection = self.db.connect()?; let mut connection = self.db.connect()?;
diesel::delete(mount_points).execute(&connection)?; diesel::delete(mount_points).execute(&mut connection)?;
diesel::insert_into(mount_points) diesel::insert_into(mount_points)
.values(mount_dirs) .values(mount_dirs)
.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822 .execute(&mut *connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
Ok(()) Ok(())
} }
} }

View file

@ -13,7 +13,7 @@ mod test;
pub use manager::*; pub use manager::*;
#[derive(Clone, Debug, Deserialize, Insertable, PartialEq, Eq, Queryable, Serialize)] #[derive(Clone, Debug, Deserialize, Insertable, PartialEq, Eq, Queryable, Serialize)]
#[table_name = "mount_points"] #[diesel(table_name = mount_points)]
pub struct MountDir { pub struct MountDir {
pub source: String, pub source: String,
pub name: String, pub name: String,

View file

@ -2,15 +2,15 @@ use anyhow::{bail, Error, Result};
use diesel::r2d2::{self, ConnectionManager, PooledConnection}; use diesel::r2d2::{self, ConnectionManager, PooledConnection};
use diesel::sqlite::SqliteConnection; use diesel::sqlite::SqliteConnection;
use diesel::RunQueryDsl; use diesel::RunQueryDsl;
use diesel_migrations::EmbeddedMigrations;
use diesel_migrations::MigrationHarness;
use std::path::Path; use std::path::Path;
mod schema; mod schema;
pub use self::schema::*; pub use self::schema::*;
#[allow(dead_code)] const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
const DB_MIGRATIONS_PATH: &str = "migrations";
embed_migrations!("migrations");
#[derive(Clone)] #[derive(Clone)]
pub struct DB { pub struct DB {
@ -56,25 +56,16 @@ impl DB {
#[allow(dead_code)] #[allow(dead_code)]
fn migrate_down(&self) -> Result<()> { fn migrate_down(&self) -> Result<()> {
let connection = self.connect().unwrap(); let mut connection = self.connect().unwrap();
loop { if let Err(e) = connection.revert_all_migrations(MIGRATIONS) {
match diesel_migrations::revert_latest_migration_in_directory( bail!(e);
&connection,
Path::new(DB_MIGRATIONS_PATH),
) {
Ok(_) => (),
Err(diesel_migrations::RunMigrationsError::MigrationError(
diesel_migrations::MigrationError::NoMigrationRun,
)) => break,
Err(e) => bail!(e),
}
} }
Ok(()) Ok(())
} }
fn migrate_up(&self) -> Result<()> { fn migrate_up(&self) -> Result<()> {
let connection = self.connect().unwrap(); let mut connection = self.connect().unwrap();
embedded_migrations::run(&connection)?; connection.run_pending_migrations(MIGRATIONS).unwrap();
Ok(()) Ok(())
} }
} }