Add tag and namespace model and socket namespace

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 4cebfc7eb2
commit 17edb0a72f

@ -446,9 +446,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.9.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
@ -668,9 +668,12 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "humantime"
version = "2.1.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "idna"
@ -811,12 +814,12 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
name = "mediarepo"
version = "0.1.0"
dependencies = [
"env_logger",
"glob",
"log",
"mediarepo-core",
"mediarepo-model",
"mediarepo-socket",
"pretty_env_logger",
"structopt",
"tokio",
"toml",
@ -1209,6 +1212,16 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger",
"log",
]
[[package]]
name = "proc-macro-crate"
version = "1.1.0"
@ -1290,6 +1303,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.10"

@ -17,7 +17,7 @@ crate-type = ["lib"]
[dependencies]
toml = {version = "0.5.8", optional=true}
structopt = {version="0.3.23", optional=true}
env_logger = {version="0.9.0", optional=true}
pretty_env_logger = {version="0.4.0", optional=true}
glob = {version="0.3.0", optional=true}
log = "0.4.14"
@ -38,5 +38,5 @@ features = ["macros", "rt-multi-thread", "io-std", "io-util"]
[features]
default = ["runtime"]
runtime = ["toml", "structopt", "mediarepo-model", "mediarepo-socket", "env_logger", "glob"]
runtime = ["toml", "structopt", "mediarepo-model", "mediarepo-socket", "pretty_env_logger", "glob"]
library = ["mediarepo-socket"]

@ -4,6 +4,7 @@ pub mod file_hash_store;
pub mod image_processing;
pub mod settings;
pub mod type_keys;
pub mod utils;
pub use futures;
pub use image;

@ -0,0 +1,7 @@
/// Parses a normalized tag into its two components of namespace and tag
pub fn parse_namespace_and_tag(norm_tag: String) -> (Option<String>, String) {
norm_tag
.split_once(':')
.map(|(n, t)| (Some(n.trim().to_string()), t.trim().to_string()))
.unwrap_or((None, norm_tag.trim().to_string()))
}

@ -1,7 +1,9 @@
pub mod file;
pub mod file_type;
pub mod hash;
pub mod namespace;
pub mod repo;
pub mod storage;
pub mod tag;
pub mod thumbnail;
pub mod type_keys;

@ -0,0 +1,62 @@
use mediarepo_core::error::RepoResult;
use mediarepo_database::entities::namespace;
use sea_orm::prelude::*;
use sea_orm::{DatabaseConnection, Set};
#[derive(Clone)]
pub struct Namespace {
db: DatabaseConnection,
model: namespace::Model,
}
impl Namespace {
pub(crate) fn new(db: DatabaseConnection, model: namespace::Model) -> Self {
Self { db, model }
}
/// Retrieves the namespace by id
pub async fn by_id(db: DatabaseConnection, id: i64) -> RepoResult<Option<Self>> {
let namespace = namespace::Entity::find_by_id(id)
.one(&db)
.await?
.map(|model| Self::new(db, model));
Ok(namespace)
}
/// Retrieves a namespace by its name
pub async fn by_name<S: AsRef<str>>(
db: DatabaseConnection,
name: S,
) -> RepoResult<Option<Self>> {
let namespace = namespace::Entity::find()
.filter(namespace::Column::Name.eq(name.as_ref()))
.one(&db)
.await?
.map(|model| Self::new(db, model));
Ok(namespace)
}
/// Adds a namespace to the database
pub async fn add<S: ToString>(db: DatabaseConnection, name: S) -> RepoResult<Self> {
let active_model = namespace::ActiveModel {
name: Set(name.to_string()),
..Default::default()
};
let active_model = active_model.insert(&db).await?;
let namespace = Self::by_id(db, active_model.id.unwrap()).await?.unwrap();
Ok(namespace)
}
/// The ID of the namespace
pub fn id(&self) -> i64 {
self.model.id
}
/// The name of the namespace
pub fn name(&self) -> &String {
&self.model.name
}
}

@ -1,9 +1,12 @@
use crate::file::File;
use crate::file_type::FileType;
use crate::namespace::Namespace;
use crate::storage::Storage;
use crate::tag::Tag;
use crate::thumbnail::Thumbnail;
use mediarepo_core::error::{RepoError, RepoResult};
use mediarepo_core::image_processing::ThumbnailSize;
use mediarepo_core::utils::parse_namespace_and_tag;
use mediarepo_database::get_database;
use sea_orm::DatabaseConnection;
use std::io::Cursor;
@ -133,6 +136,55 @@ impl Repo {
Ok(())
}
/// Returns all tags stored in the database
pub async fn tags(&self) -> RepoResult<Vec<Tag>> {
Tag::all(self.db.clone()).await
}
/// Adds or finds a tag
pub async fn add_or_find_tag<S: ToString>(&self, tag: S) -> RepoResult<Tag> {
let (namespace, name) = parse_namespace_and_tag(tag.to_string());
if let Some(namespace) = namespace {
self.add_or_find_namespaced_tag(name, namespace).await
} else {
self.add_or_find_unnamespaced_tag(name).await
}
}
/// Adds or finds an unnamespaced tag
async fn add_or_find_unnamespaced_tag(&self, name: String) -> RepoResult<Tag> {
if let Some(tag) = Tag::by_name(self.db.clone(), &name).await? {
Ok(tag)
} else {
self.add_unnamespaced_tag(name).await
}
}
/// Adds an unnamespaced tag
async fn add_unnamespaced_tag(&self, name: String) -> RepoResult<Tag> {
Tag::add(self.db.clone(), name, None).await
}
/// Adds or finds a namespaced tag
async fn add_or_find_namespaced_tag(&self, name: String, namespace: String) -> RepoResult<Tag> {
if let Some(tag) = Tag::by_name_and_namespace(self.db.clone(), &name, &namespace).await? {
Ok(tag)
} else {
self.add_namespaced_tag(name, namespace).await
}
}
/// Adds a namespaced tag
async fn add_namespaced_tag(&self, name: String, namespace: String) -> RepoResult<Tag> {
let namespace =
if let Some(namespace) = Namespace::by_name(self.db.clone(), &namespace).await? {
namespace
} else {
Namespace::add(self.db.clone(), namespace).await?
};
Tag::add(self.db.clone(), name, Some(namespace.id())).await
}
fn get_main_storage(&self) -> RepoResult<&Storage> {
if let Some(storage) = &self.main_storage {
Ok(storage)

@ -0,0 +1,119 @@
use crate::namespace::Namespace;
use mediarepo_core::error::RepoResult;
use mediarepo_database::entities::namespace;
use mediarepo_database::entities::tag;
use sea_orm::prelude::*;
use sea_orm::QuerySelect;
use sea_orm::{DatabaseConnection, JoinType, Set};
#[derive(Clone)]
pub struct Tag {
db: DatabaseConnection,
model: tag::Model,
namespace: Option<namespace::Model>,
}
impl Tag {
pub(crate) fn new(
db: DatabaseConnection,
model: tag::Model,
namespace: Option<namespace::Model>,
) -> Self {
Self {
db,
model,
namespace,
}
}
/// Returns all tags stored in the database
pub async fn all(db: DatabaseConnection) -> RepoResult<Vec<Self>> {
let tags: Vec<Self> = tag::Entity::find()
.find_also_related(namespace::Entity)
.all(&db)
.await?
.into_iter()
.map(|(tag, namespace)| Self::new(db.clone(), tag, namespace))
.collect();
Ok(tags)
}
/// Returns the tag by id
pub async fn by_id(db: DatabaseConnection, id: i64) -> RepoResult<Option<Self>> {
let tag = tag::Entity::find_by_id(id)
.find_also_related(namespace::Entity)
.one(&db)
.await?
.map(|(model, namespace)| Self::new(db, model, namespace));
Ok(tag)
}
/// Retrieves the unnamespaced tag by name
pub async fn by_name<S: AsRef<str>>(
db: DatabaseConnection,
name: S,
) -> RepoResult<Option<Self>> {
let tag = tag::Entity::find()
.filter(tag::Column::Name.eq(name.as_ref()))
.filter(tag::Column::NamespaceId.eq(Option::<i64>::None))
.one(&db)
.await?
.map(|t| Tag::new(db, t, None));
Ok(tag)
}
/// Retrieves the namespaced tag by name and namespace
pub async fn by_name_and_namespace<S1: AsRef<str>, S2: AsRef<str>>(
db: DatabaseConnection,
name: S1,
namespace: S2,
) -> RepoResult<Option<Self>> {
let tag = tag::Entity::find()
.find_also_related(namespace::Entity)
.join(JoinType::InnerJoin, namespace::Relation::Tag.def())
.filter(namespace::Column::Name.eq(namespace.as_ref()))
.filter(tag::Column::Name.eq(name.as_ref()))
.one(&db)
.await?
.map(|(t, n)| Self::new(db.clone(), t, n));
Ok(tag)
}
/// Adds a new tag to the database
pub async fn add<S: ToString>(
db: DatabaseConnection,
name: S,
namespace_id: Option<i64>,
) -> RepoResult<Self> {
let active_model = tag::ActiveModel {
name: Set(name.to_string()),
namespace_id: Set(namespace_id),
..Default::default()
};
let active_model = active_model.insert(&db).await?;
let tag = Self::by_id(db, active_model.id.unwrap()).await?.unwrap();
Ok(tag)
}
/// The ID of the tag
pub fn id(&self) -> i64 {
self.model.id
}
/// The name of the tag
pub fn name(&self) -> &String {
&self.model.name
}
/// The namespace of the tag
pub fn namespace(&self) -> Option<Namespace> {
self.namespace
.clone()
.map(|n| Namespace::new(self.db.clone(), n))
}
}

@ -1,7 +1,10 @@
use mediarepo_core::rmp_ipc::{namespace, namespace::Namespace, IPCBuilder};
pub mod files;
pub mod tags;
pub fn build_namespaces(builder: IPCBuilder) -> IPCBuilder {
builder.add_namespace(namespace!(files::FilesNamespace))
builder
.add_namespace(namespace!(files::FilesNamespace))
.add_namespace(namespace!(tags::TagsNamespace))
}

@ -0,0 +1,34 @@
use crate::types::responses::TagResponse;
use crate::utils::get_repo_from_context;
use mediarepo_core::rmp_ipc::prelude::*;
pub struct TagsNamespace;
impl NamespaceProvider for TagsNamespace {
fn name() -> &'static str {
"tags"
}
fn register(handler: &mut EventHandler) {
events!(handler,
"all_tags" => Self::all_tags
);
}
}
impl TagsNamespace {
async fn all_tags(ctx: &Context, event: Event) -> IPCResult<()> {
let repo = get_repo_from_context(ctx).await;
let tags: Vec<TagResponse> = repo
.tags()
.await?
.into_iter()
.map(TagResponse::from)
.collect();
ctx.emitter
.emit_response_to(event.id(), Self::name(), "all_tags", tags)
.await?;
Ok(())
}
}

@ -1,6 +1,7 @@
use chrono::NaiveDateTime;
use mediarepo_model::file::File;
use mediarepo_model::file_type::FileType;
use mediarepo_model::tag::Tag;
use mediarepo_model::thumbnail::Thumbnail;
use serde::{Deserialize, Serialize};
@ -55,3 +56,20 @@ pub struct InfoResponse {
pub name: String,
pub version: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TagResponse {
pub id: i64,
pub name: String,
pub namespace: Option<String>,
}
impl From<Tag> for TagResponse {
fn from(tag: Tag) -> Self {
Self {
id: tag.id(),
name: tag.name().to_owned(),
namespace: tag.namespace().map(|n| n.name().to_owned()),
}
}
}

@ -3,6 +3,7 @@ mod utils;
use crate::constants::{DEFAULT_STORAGE_NAME, SETTINGS_PATH, THUMBNAIL_STORAGE_NAME};
use crate::utils::{create_paths_for_repo, get_repo, load_settings};
use log::LevelFilter;
use mediarepo_core::error::RepoResult;
use mediarepo_core::futures::future;
use mediarepo_core::settings::Settings;
@ -10,7 +11,10 @@ use mediarepo_core::type_keys::SettingsKey;
use mediarepo_model::repo::Repo;
use mediarepo_model::type_keys::RepoKey;
use mediarepo_socket::get_builder;
use pretty_env_logger::env_logger::WriteStyle;
use std::env;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use structopt::StructOpt;
use tokio::fs;
@ -66,6 +70,7 @@ fn main() -> RepoResult<()> {
}
fn get_single_thread_runtime() -> Runtime {
log::info!("Using current thread runtime");
runtime::Builder::new_current_thread()
.enable_all()
.max_blocking_threads(1)
@ -74,6 +79,7 @@ fn get_single_thread_runtime() -> Runtime {
}
fn get_multi_thread_runtime() -> Runtime {
log::info!("Using multi thread runtime");
runtime::Builder::new_multi_thread()
.enable_all()
.build()
@ -81,7 +87,15 @@ fn get_multi_thread_runtime() -> Runtime {
}
fn build_logger() {
env_logger::builder()
pretty_env_logger::formatted_timed_builder()
.filter(
None,
env::var("RUST_LOG")
.ok()
.and_then(|level| LevelFilter::from_str(&level).ok())
.unwrap_or(LevelFilter::Info),
)
.write_style(WriteStyle::Always)
.filter_module("sqlx", log::LevelFilter::Warn)
.filter_module("tokio", log::LevelFilter::Info)
.filter_module("tracing", log::LevelFilter::Warn)

Loading…
Cancel
Save