Compare commits

...

2 commits

Author SHA1 Message Date
0f876324aa Move to connection pools 2023-06-26 12:09:17 -07:00
f522b96116 Move sample list to separate file 2023-06-26 11:45:17 -07:00
10 changed files with 235 additions and 133 deletions

53
Cargo.lock generated
View file

@ -113,6 +113,7 @@ checksum = "f7a532c1f99a0f596f6960a60d1e119e91582b24b39e2d83a190e61262c3ef0c"
dependencies = [ dependencies = [
"diesel_derives", "diesel_derives",
"libsqlite3-sys", "libsqlite3-sys",
"r2d2",
"time", "time",
] ]
@ -798,6 +799,29 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.0" version = "1.1.0"
@ -898,6 +922,26 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r2d2"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
dependencies = [
"log",
"parking_lot",
"scheduled-thread-pool",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "relm4" name = "relm4"
version = "0.6.0" version = "0.6.0"
@ -993,6 +1037,15 @@ dependencies = [
"relm4-icons", "relm4-icons",
] ]
[[package]]
name = "scheduled-thread-pool"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
dependencies = [
"parking_lot",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"

View file

@ -10,7 +10,7 @@ gtk = { version = "0.6.6", package = "gtk4" }
relm4 = { version = "0.6.0", features = ["libadwaita"] } relm4 = { version = "0.6.0", features = ["libadwaita"] }
relm4-components = "0.6.0" relm4-components = "0.6.0"
relm4-icons = { version = "0.6.0", features = ["plus", "minus"] } relm4-icons = { version = "0.6.0", features = ["plus", "minus"] }
diesel = { version = "2.1.0", features = ["sqlite", "returning_clauses_for_sqlite_3_35"] } diesel = { version = "2.1.0", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "r2d2"] }
libsqlite3-sys = { version = "0.26.0", features = ["bundled"] } libsqlite3-sys = { version = "0.26.0", features = ["bundled"] }
dotenvy = "0.15" dotenvy = "0.15"
nom = "7.1.3" nom = "7.1.3"

View file

@ -5,7 +5,7 @@ fn main() {
let suggestions = let suggestions =
TagSuggestions::read_from_file("tag_terms.txt").expect("expected tag file to load"); TagSuggestions::read_from_file("tag_terms.txt").expect("expected tag file to load");
let connection = &mut establish_connection(); let connection = &mut get_connection_pool().get().expect("Expected a connection");
let file_str = env::args().nth(1).expect("Need just one argument."); let file_str = env::args().nth(1).expect("Need just one argument.");
let tags = suggestions.get_suggestions(&file_str); let tags = suggestions.get_suggestions(&file_str);

View file

@ -2,7 +2,7 @@ use sample_amp::*;
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
fn main() { fn main() {
let connection = &mut establish_connection(); let connection = &mut get_connection_pool().get().expect("Expected a connection.");
let mut name = String::new(); let mut name = String::new();

View file

@ -1,5 +1,8 @@
use itertools::Itertools;
use sample_amp::{get_connection_pool, list_tags};
fn main() { fn main() {
let connection = &mut establish_connection(); let connection = &mut get_connection_pool().get().expect("Expected a connection");
println!( println!(
"Found tags: {}", "Found tags: {}",
list_tags(connection).iter().map(|t| &t.name).join(", ") list_tags(connection).iter().map(|t| &t.name).join(", ")

View file

@ -1,8 +1,8 @@
use itertools::Itertools; use itertools::Itertools;
use sample_amp::{establish_connection, list_tags}; use sample_amp::{get_connection_pool, list_tags};
fn main() { fn main() {
let connection = &mut establish_connection(); let connection = &mut get_connection_pool().get().expect("Expected a connection.");
println!( println!(
"Found tags: {}", "Found tags: {}",
list_tags(connection).iter().map(|t| &t.name).join(", ") list_tags(connection).iter().map(|t| &t.name).join(", ")

View file

@ -5,7 +5,7 @@ use sample_amp::*;
fn main() { fn main() {
use self::schema::libraries::dsl::*; use self::schema::libraries::dsl::*;
let connection = &mut establish_connection(); let connection = &mut get_connection_pool().get().expect("Expected a connection");
let results = libraries let results = libraries
.limit(5) .limit(5)
.select(Library::as_select()) .select(Library::as_select())

View file

@ -1,4 +1,7 @@
use diesel::prelude::*; use diesel::{
prelude::*,
r2d2::{ConnectionManager, Pool},
};
use dotenvy::dotenv; use dotenvy::dotenv;
use models::{NewSample, Sample, Tag}; use models::{NewSample, Sample, Tag};
use std::{env, path::Path}; use std::{env, path::Path};
@ -9,12 +12,17 @@ pub mod models;
pub mod schema; pub mod schema;
pub mod tag_terms; pub mod tag_terms;
pub fn establish_connection() -> SqliteConnection { pub fn get_connection_pool() -> Pool<ConnectionManager<SqliteConnection>> {
dotenv().ok(); dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
SqliteConnection::establish(&database_url)
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) let manager = ConnectionManager::<SqliteConnection>::new(database_url);
Pool::builder()
.test_on_check_out(true)
.build(manager)
.expect("Could not build connection pool")
} }
pub fn create_library(conn: &mut SqliteConnection, name: &str) -> Library { pub fn create_library(conn: &mut SqliteConnection, name: &str) -> Library {

View file

@ -1,36 +1,70 @@
use diesel::prelude::*; use diesel::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool};
use gtk::gdk::Display; use gtk::gdk::Display;
use gtk::glib::BoxedAnyObject;
use gtk::prelude::*; use gtk::prelude::*;
use relm4::{factory::FactoryVecDeque, prelude::*}; use relm4::{factory::FactoryVecDeque, prelude::*};
use std::cell::Ref;
mod library_list; mod library_list;
mod sample_list;
use library_list::LibraryListItem; use library_list::LibraryListItem;
mod app_msg; mod app_msg;
use app_msg::AppMsg; use app_msg::AppMsg;
use sample_amp::establish_connection; use sample_amp::get_connection_pool;
use sample_amp::models::Library; use sample_amp::models::Library;
use sample_amp::schema::libraries; use sample_amp::schema::libraries;
struct AppModel { use crate::sample_list::SampleListModel;
samples: gtk::MultiSelection,
libraries: FactoryVecDeque<LibraryListItem>,
connection: SqliteConnection,
}
struct Sample { struct AppModel {
name: String, sample_list: Controller<SampleListModel>,
bpm: f32, libraries: FactoryVecDeque<LibraryListItem>,
key: String, // todo: key struct? pool: Pool<ConnectionManager<SqliteConnection>>,
} }
#[relm4::component] #[relm4::component]
impl SimpleComponent for AppModel { impl SimpleComponent for AppModel {
type Input = AppMsg;
type Output = ();
type Init = (); type Init = ();
type Input = AppMsg; fn init(
type Output = (); _: Self::Init,
window: &Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let mut model = AppModel {
pool: get_connection_pool(),
libraries: FactoryVecDeque::new(gtk::ListBox::default(), sender.input_sender()),
sample_list: SampleListModel::builder().launch(()).detach(),
};
let sample_view = model.sample_list.widget();
// todo: cleaner
let library_results = libraries::dsl::libraries
.select(Library::as_select())
.load(&mut model.pool.get().expect("Expected a connection."))
.expect("Error loading libraries");
model.libraries.guard().push_back("All".to_string());
for lib in library_results {
model.libraries.guard().push_back(lib.name);
}
let library_view = model.libraries.widget();
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
match message {
AppMsg::Close => {
relm4::main_application().quit();
}
}
}
view! { view! {
gtk::Window { gtk::Window {
@ -56,31 +90,8 @@ impl SimpleComponent for AppModel {
set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
set_hexpand: true, set_hexpand: true,
gtk::ScrolledWindow { #[local_ref]
set_vexpand: true, sample_view -> gtk::ScrolledWindow {},
set_css_classes: &["sample-list-view"],
gtk::ColumnView {
set_model: Some(&model.samples),
append_column = &gtk::ColumnViewColumn {
set_title: Some("Name"),
set_resizable: true,
set_expand: true,
set_factory: Some(&factory_name)
},
append_column = &gtk::ColumnViewColumn {
set_title: Some("BPM"),
set_factory: Some(&factory_bpm)
},
append_column = &gtk::ColumnViewColumn {
set_title: Some("Key"),
set_factory: Some(&factory_key)
},
append_column = &gtk::ColumnViewColumn {
set_title: Some("Tags"),
set_resizable: true,
}
}
},
gtk::Box { gtk::Box {
set_height_request: 100, set_height_request: 100,
@ -94,89 +105,6 @@ impl SimpleComponent for AppModel {
} }
} }
} }
fn init(
_: Self::Init,
window: &Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let factory_name = gtk::SignalListItemFactory::new();
factory_name.connect_bind(move |_factory, item| {
let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let obj = item.item().and_downcast::<BoxedAnyObject>().unwrap();
let sample: Ref<Sample> = obj.borrow();
let cell = gtk::Label::new(Some(&sample.name.to_string()));
cell.set_halign(gtk::Align::Start);
item.set_child(Some(&cell))
});
let factory_bpm = gtk::SignalListItemFactory::new();
factory_bpm.connect_bind(move |_factory, item| {
let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let obj = item.item().and_downcast::<BoxedAnyObject>().unwrap();
let sample: Ref<Sample> = obj.borrow();
let cell = gtk::Label::new(Some(&sample.bpm.to_string()));
cell.set_halign(gtk::Align::End);
item.set_child(Some(&cell))
});
let factory_key = gtk::SignalListItemFactory::new();
factory_key.connect_bind(move |_factory, item| {
let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let obj = item.item().and_downcast::<BoxedAnyObject>().unwrap();
let sample: Ref<Sample> = obj.borrow();
let cell = gtk::Label::new(Some(&sample.key.to_string()));
cell.set_halign(gtk::Align::Start);
item.set_child(Some(&cell))
});
let samples = gtk::gio::ListStore::new(gtk::glib::types::Type::OBJECT);
let sample_a = BoxedAnyObject::new(Sample {
name: "01 MOOLOO-108 bpm a.wav".to_string(),
bpm: 108.0,
key: "A".to_string(),
});
samples.append(&sample_a);
let sample_b = BoxedAnyObject::new(Sample {
name: "03 FAST AND FURIOUS-144.3 bpm a.wav".to_string(),
bpm: 144.3,
key: "A".to_string(),
});
samples.append(&sample_b);
let samples = gtk::MultiSelection::new(Some(samples));
let mut model = AppModel {
samples,
connection: establish_connection(),
libraries: FactoryVecDeque::new(gtk::ListBox::default(), sender.input_sender()),
};
// todo: cleaner
let library_results = libraries::dsl::libraries
.select(Library::as_select())
.load(&mut model.connection)
.expect("Error loading libraries");
model.libraries.guard().push_back("All".to_string());
for lib in library_results {
model.libraries.guard().push_back(lib.name);
}
let library_view = model.libraries.widget();
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
match message {
AppMsg::Close => {
relm4::main_application().quit();
}
}
}
} }
fn load_css() { fn load_css() {

110
src/sample_list.rs Normal file
View file

@ -0,0 +1,110 @@
use std::cell::Ref;
use gtk::{glib::BoxedAnyObject, prelude::*};
use relm4::prelude::*;
struct Sample {
name: String,
bpm: f32,
key: String, // todo: key struct?
}
#[derive(Debug)]
pub struct SampleListMsg {}
pub struct SampleListModel {
samples: gtk::MultiSelection,
}
#[relm4::component(pub)]
impl SimpleComponent for SampleListModel {
type Input = SampleListMsg;
type Output = ();
type Init = ();
view! {
#[root]
gtk::ScrolledWindow {
set_vexpand: true,
set_css_classes: &["sample-list-view"],
gtk::ColumnView {
set_model: Some(&model.samples),
append_column = &gtk::ColumnViewColumn {
set_title: Some("Name"),
set_resizable: true,
set_expand: true,
set_factory: Some(&factory_name)
},
append_column = &gtk::ColumnViewColumn {
set_title: Some("BPM"),
set_factory: Some(&factory_bpm)
},
append_column = &gtk::ColumnViewColumn {
set_title: Some("Key"),
set_factory: Some(&factory_key)
},
append_column = &gtk::ColumnViewColumn {
set_title: Some("Tags"),
set_resizable: true,
}
}
}
}
fn init(
_: Self::Init,
window: &Self::Root,
_sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let factory_name = gtk::SignalListItemFactory::new();
factory_name.connect_bind(move |_factory, item| {
let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let obj = item.item().and_downcast::<BoxedAnyObject>().unwrap();
let sample: Ref<Sample> = obj.borrow();
let cell = gtk::Label::new(Some(&sample.name.to_string()));
cell.set_halign(gtk::Align::Start);
item.set_child(Some(&cell))
});
let factory_bpm = gtk::SignalListItemFactory::new();
factory_bpm.connect_bind(move |_factory, item| {
let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let obj = item.item().and_downcast::<BoxedAnyObject>().unwrap();
let sample: Ref<Sample> = obj.borrow();
let cell = gtk::Label::new(Some(&sample.bpm.to_string()));
cell.set_halign(gtk::Align::End);
item.set_child(Some(&cell))
});
let factory_key = gtk::SignalListItemFactory::new();
factory_key.connect_bind(move |_factory, item| {
let item = item.downcast_ref::<gtk::ListItem>().unwrap();
let obj = item.item().and_downcast::<BoxedAnyObject>().unwrap();
let sample: Ref<Sample> = obj.borrow();
let cell = gtk::Label::new(Some(&sample.key.to_string()));
cell.set_halign(gtk::Align::Start);
item.set_child(Some(&cell))
});
let samples = gtk::gio::ListStore::new(gtk::glib::types::Type::OBJECT);
let sample_a = BoxedAnyObject::new(Sample {
name: "01 MOOLOO-108 bpm a.wav".to_string(),
bpm: 108.0,
key: "A".to_string(),
});
samples.append(&sample_a);
let sample_b = BoxedAnyObject::new(Sample {
name: "03 FAST AND FURIOUS-144.3 bpm a.wav".to_string(),
bpm: 144.3,
key: "A".to_string(),
});
samples.append(&sample_b);
let samples = gtk::MultiSelection::new(Some(samples));
let model = SampleListModel { samples };
let widgets = view_output!();
ComponentParts { model, widgets }
}
}