Messy code to store samples and test that out, better errors to come

This commit is contained in:
Vivianne 2023-06-15 03:30:13 -07:00
parent 7616cbd4ca
commit 0599148543
9 changed files with 220 additions and 12 deletions

View file

@ -0,0 +1,3 @@
DROP TABLE samples_tags;
DROP TABLE tags;
DROP TABLE samples;

View file

@ -0,0 +1,20 @@
CREATE TABLE samples (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR NOT NULL,
path VARCHAR NOT NULL,
bpm REAL,
key VARCHAR
);
CREATE TABLE tags (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR NOT NULL
);
CREATE TABLE samples_tags (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
sample_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (sample_id) REFERENCES samples (id),
FOREIGN KEY (tag_id) REFERENCES tags (id)
);

14
src/bin/add_sample.rs Normal file
View file

@ -0,0 +1,14 @@
use sample_amp::{tag_terms::TagSuggestions, *};
use std::{env, path::Path};
fn main() {
let suggestions =
TagSuggestions::read_from_file("tag_terms.txt").expect("expected tag file to load");
let connection = &mut establish_connection();
let file_str = env::args().nth(1).expect("Need just one argument.");
let tags = suggestions.get_suggestions(&file_str);
let sample = add_sample(connection, Path::new(&file_str), tags);
dbg!(sample);
}

7
src/bin/list_samples.rs Normal file
View file

@ -0,0 +1,7 @@
fn main() {
let connection = &mut establish_connection();
println!(
"Found tags: {}",
list_tags(connection).iter().map(|t| &t.name).join(", ")
);
}

10
src/bin/list_tags.rs Normal file
View file

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

View file

@ -1,8 +1,9 @@
use diesel::prelude::*;
use dotenvy::dotenv;
use std::env;
use models::{NewSample, Sample, Tag};
use std::{env, path::Path};
use crate::models::{Library, NewLibrary};
use crate::models::{Library, NewLibrary, NewSampleTag, NewTag, SampleTag};
pub mod models;
pub mod schema;
@ -27,3 +28,80 @@ pub fn create_library(conn: &mut SqliteConnection, name: &str) -> Library {
.get_result(conn)
.expect("Error saving new post")
}
pub fn get_tag<'a>(conn: &mut SqliteConnection, tag: &'a str) -> Option<Tag> {
use crate::schema::tags::dsl::*;
tags.filter(name.eq(&tag))
.select(Tag::as_select())
.first(conn)
.ok()
}
pub fn list_tags(conn: &mut SqliteConnection) -> Vec<Tag> {
use crate::schema::tags::dsl::*;
tags.select(Tag::as_select())
.get_results(conn)
.expect("can't list tags")
}
pub fn samples_with_tag<'a>(conn: &mut SqliteConnection, tag: &'a str) -> Vec<Sample> {
let tag = get_tag(conn, tag).expect("Expected tag to exist...");
let sample_ids = SampleTag::belonging_to(&tag).select(schema::samples_tags::sample_id);
schema::samples::table
.filter(schema::samples::id.eq_any(sample_ids))
.select(Sample::as_select())
.get_results(conn)
.expect("expected sample")
}
pub fn add_sample<'a>(
conn: &mut SqliteConnection,
path: &Path,
tags: impl Iterator<Item = &'a String>,
) -> (Sample, Vec<Tag>) {
use crate::schema::{samples, samples_tags, tags};
let name = path.file_name().expect("expected file name");
let new_sample = NewSample {
name: &name.to_string_lossy(),
path: &path.to_string_lossy(),
bpm: None,
key: None,
};
let sample = diesel::insert_into(samples::table)
.values(&new_sample)
.returning(Sample::as_returning())
.get_result(conn)
.expect("Error saving new sample");
let tags: Vec<Tag> = tags
.into_iter()
.map(|tag_name| {
let tag = get_tag(conn, tag_name).unwrap_or_else(|| {
let tag = NewTag { name: tag_name };
diesel::insert_into(tags::table)
.values(&tag)
.returning(Tag::as_returning())
.get_result(conn)
.expect("Error saving tag")
});
let sample_tag = NewSampleTag {
tag_id: tag.id,
sample_id: sample.id,
};
diesel::insert_into(samples_tags::table)
.values(&sample_tag)
.returning(SampleTag::as_returning())
.get_result(conn)
.expect("Error adding sample_tag");
tag
})
.collect();
(sample, tags)
}

View file

@ -1,7 +1,7 @@
use super::schema::libraries;
use super::schema::{libraries, samples, samples_tags, tags};
use diesel::prelude::*;
#[derive(Queryable, Selectable)]
#[derive(Debug, Queryable, Selectable)]
#[diesel(table_name = libraries)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
pub struct Library {
@ -9,8 +9,57 @@ pub struct Library {
pub name: String,
}
#[derive(Insertable)]
#[derive(Debug, Insertable)]
#[diesel(table_name = libraries)]
pub struct NewLibrary<'a> {
pub name: &'a str,
}
#[derive(Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = samples)]
pub struct Sample {
pub id: i32,
pub name: String,
pub path: String,
pub bpm: Option<f32>,
pub key: Option<String>, // For now this seems to be reasonable, instead of creating a giant enum.
}
#[derive(Debug, Insertable)]
#[diesel(table_name = samples)]
pub struct NewSample<'a> {
pub name: &'a str,
pub path: &'a str,
pub bpm: Option<f32>,
pub key: Option<&'a str>,
}
#[derive(Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = tags)]
pub struct Tag {
pub id: i32,
pub name: String,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = tags)]
pub struct NewTag<'a> {
pub name: &'a str,
}
#[derive(Debug, Identifiable, Associations, Queryable, Selectable)]
#[diesel(belongs_to(Sample))]
#[diesel(belongs_to(Tag))]
#[diesel(table_name = samples_tags)]
pub struct SampleTag {
pub id: i32,
pub sample_id: i32,
pub tag_id: i32,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = samples_tags)]
pub struct NewSampleTag {
pub sample_id: i32,
pub tag_id: i32,
}

View file

@ -6,3 +6,33 @@ diesel::table! {
name -> Text,
}
}
diesel::table! {
samples (id) {
id -> Integer,
name -> Text,
path -> Text,
bpm -> Nullable<Float>,
key -> Nullable<Text>,
}
}
diesel::table! {
samples_tags (id) {
id -> Integer,
sample_id -> Integer,
tag_id -> Integer,
}
}
diesel::table! {
tags (id) {
id -> Integer,
name -> Text,
}
}
diesel::joinable!(samples_tags -> samples (sample_id));
diesel::joinable!(samples_tags -> tags (tag_id));
diesel::allow_tables_to_appear_in_same_query!(libraries, samples, samples_tags, tags,);

View file

@ -54,13 +54,10 @@ impl TagSuggestions {
self.map.get(input).unwrap_or(input)
}
pub fn get_suggestions<'a>(
&'a self,
input_list: impl IntoIterator<Item = &'a str>,
) -> impl Iterator<Item = &String> {
input_list
.into_iter()
.filter_map(|i| self.map.get(i))
pub fn get_suggestions<'a>(&'a self, input: &'a str) -> impl Iterator<Item = &String> {
input
.split([' ', std::path::MAIN_SEPARATOR, '_', '-', '.']) // todo: pascal case detection
.filter_map(|i| self.map.get(&i.to_lowercase()))
.unique()
}
}