Refactor structure
This commit is contained in:
16
models/Cargo.toml
Executable file
16
models/Cargo.toml
Executable file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "models"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
directories.workspace = true
|
||||
color-eyre.workspace = true
|
||||
serde.workspace = true
|
||||
lazy_static.workspace = true
|
||||
ratatui.workspace = true
|
||||
serde_json.workspace = true
|
||||
dashmap.workspace = true
|
||||
db = { path = "../db" }
|
||||
language-tags = { version = "0.3.2", features = ["serde"] }
|
||||
sys-locale = "0.3.2"
|
||||
72
models/src/config.rs
Executable file
72
models/src/config.rs
Executable file
@@ -0,0 +1,72 @@
|
||||
use std::path::PathBuf;
|
||||
use language_tags::LanguageTag;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use color_eyre::Result;
|
||||
use crate::{APP_CONIFG_FILE_PATH, CACHE_MAP};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ApplicationConfig {
|
||||
pub basic_config: BasicConfig,
|
||||
pub path_config: PathConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BasicConfig {
|
||||
pub locale: LanguageTag,
|
||||
pub tick_rate: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PathConfig {
|
||||
pub dlsite_paths: Vec<String>
|
||||
}
|
||||
|
||||
const CONFIG_KEY: &str = "app_conf";
|
||||
|
||||
impl ApplicationConfig {
|
||||
pub fn get_config() -> Result<Self> {
|
||||
if CACHE_MAP.contains_key(CONFIG_KEY) &&
|
||||
let Some(cached_config) = CACHE_MAP.get(CONFIG_KEY)
|
||||
{
|
||||
Ok(serde_json::from_value(cached_config.clone())?)
|
||||
} else if APP_CONIFG_FILE_PATH.exists() {
|
||||
ApplicationConfig::from_file(&APP_CONIFG_FILE_PATH)
|
||||
} else {
|
||||
ApplicationConfig::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn from_file(path: &PathBuf) -> Result<Self> {
|
||||
let reader = std::fs::File::open(path)?;
|
||||
let result: serde_json::Value = serde_json::from_reader(reader)?;
|
||||
CACHE_MAP.insert(CONFIG_KEY.to_string(), result.clone());
|
||||
Ok(serde_json::from_value(result)?)
|
||||
}
|
||||
|
||||
fn new() -> Result<Self> {
|
||||
let default_locale = sys_locale::get_locale().unwrap_or(String::from("ja-JP"));
|
||||
let conf = Self {
|
||||
basic_config: BasicConfig {
|
||||
tick_rate: 250,
|
||||
locale: LanguageTag::parse(&default_locale)?,
|
||||
},
|
||||
path_config: PathConfig {
|
||||
dlsite_paths: vec![],
|
||||
},
|
||||
};
|
||||
conf.clone().write_to_file(APP_CONIFG_FILE_PATH.to_path_buf())?;
|
||||
Ok(conf)
|
||||
}
|
||||
|
||||
fn write_to_file(self, path: PathBuf) -> Result<()> {
|
||||
let writer = std::fs::File::create(path)?;
|
||||
serde_json::to_writer_pretty(writer, &self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save(self) -> Result<()> {
|
||||
let current_value = serde_json::to_value(&self)?;
|
||||
CACHE_MAP.alter(CONFIG_KEY, |_, _| current_value);
|
||||
self.write_to_file(APP_CONIFG_FILE_PATH.to_path_buf())
|
||||
}
|
||||
}
|
||||
55
models/src/dlsite/category.rs
Executable file
55
models/src/dlsite/category.rs
Executable file
@@ -0,0 +1,55 @@
|
||||
use color_eyre::Report;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use db::types::{RocksColumn, RocksReferences};
|
||||
use crate::config::ApplicationConfig;
|
||||
use crate::dlsite::genre::DLSiteGenre;
|
||||
use crate::dlsite::translation::DLSiteTranslation;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DLSiteCategory {
|
||||
#[serde(skip)]
|
||||
pub id: String,
|
||||
pub genre_ids: Vec<u16>,
|
||||
pub name: DLSiteTranslation
|
||||
}
|
||||
|
||||
impl TryFrom<super::crawler::DLSiteGenreCategory> for DLSiteCategory {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(value: super::crawler::DLSiteGenreCategory) -> Result<Self, Self::Error> {
|
||||
let category = Self {
|
||||
id: format!(
|
||||
"{}/{}",
|
||||
value.id,
|
||||
ApplicationConfig::get_config()?.basic_config.locale.primary_language()
|
||||
),
|
||||
genre_ids: value.values.iter()
|
||||
.map(|v| v.value.parse::<u16>())
|
||||
.filter_map(Result::ok)
|
||||
.collect(),
|
||||
name: DLSiteTranslation::try_from(value.category_name.as_str())?,
|
||||
};
|
||||
Ok(category)
|
||||
}
|
||||
}
|
||||
|
||||
impl RocksReferences<DLSiteGenre> for DLSiteCategory {
|
||||
fn get_reference_ids(&self) -> Vec<<DLSiteGenre as RocksColumn>::Id> {
|
||||
self.genre_ids.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl RocksColumn for DLSiteCategory {
|
||||
type Id = String;
|
||||
fn get_id(&self) -> Self::Id {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: Self::Id) {
|
||||
self.id = id;
|
||||
}
|
||||
|
||||
fn get_column_name() -> String {
|
||||
String::from("dl_categories")
|
||||
}
|
||||
}
|
||||
39
models/src/dlsite/crawler.rs
Executable file
39
models/src/dlsite/crawler.rs
Executable file
@@ -0,0 +1,39 @@
|
||||
use std::path::PathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct DLSiteManiax {
|
||||
#[serde(rename = "work_name")]
|
||||
pub title: String,
|
||||
#[serde(rename = "work_image")]
|
||||
pub work_image_url: String,
|
||||
#[serde(rename = "dl_count")]
|
||||
pub sells_count: u32,
|
||||
#[serde(skip)]
|
||||
pub genre_ids: Vec<u16>,
|
||||
#[serde(skip)]
|
||||
pub rj_num: String,
|
||||
#[serde(skip)]
|
||||
pub folder_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct DLSiteFilter {
|
||||
pub genre_all: Value
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct DLSiteGenreCategory {
|
||||
pub category_name: String,
|
||||
pub values: Vec<DLSiteGenre>,
|
||||
#[serde(skip)]
|
||||
pub id: u8
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct DLSiteGenre {
|
||||
pub value: String,
|
||||
pub name: String
|
||||
}
|
||||
26
models/src/dlsite/genre.rs
Executable file
26
models/src/dlsite/genre.rs
Executable file
@@ -0,0 +1,26 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use db::types::RocksColumn;
|
||||
use super::translation::DLSiteTranslation;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DLSiteGenre {
|
||||
#[serde(skip)]
|
||||
pub id: u16,
|
||||
pub name: Vec<DLSiteTranslation>
|
||||
}
|
||||
|
||||
impl RocksColumn for DLSiteGenre {
|
||||
type Id = u16;
|
||||
|
||||
fn get_id(&self) -> Self::Id {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: Self::Id) {
|
||||
self.id = id;
|
||||
}
|
||||
|
||||
fn get_column_name() -> String {
|
||||
String::from("dl_genres")
|
||||
}
|
||||
}
|
||||
59
models/src/dlsite/maniax.rs
Executable file
59
models/src/dlsite/maniax.rs
Executable file
@@ -0,0 +1,59 @@
|
||||
use std::path::PathBuf;
|
||||
use ratatui::text::Text;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use db::types::{RocksColumn, RocksReferences};
|
||||
use super::genre::DLSiteGenre;
|
||||
use super::translation::DLSiteTranslation;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DLSiteManiax {
|
||||
#[serde(skip)]
|
||||
pub rj_num: String,
|
||||
pub genre_ids: Vec<u16>,
|
||||
pub name: Vec<DLSiteTranslation>,
|
||||
pub sells_count: u32,
|
||||
pub folder_path: PathBuf,
|
||||
pub version: Option<String>
|
||||
}
|
||||
|
||||
impl From<super::crawler::DLSiteManiax> for DLSiteManiax {
|
||||
fn from(value: super::crawler::DLSiteManiax) -> Self {
|
||||
let title = DLSiteTranslation::try_from(value.title.as_str()).unwrap();
|
||||
Self {
|
||||
rj_num: value.rj_num,
|
||||
genre_ids: value.genre_ids,
|
||||
name: vec![title],
|
||||
sells_count: value.sells_count,
|
||||
folder_path: value.folder_path,
|
||||
version: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RocksColumn for DLSiteManiax {
|
||||
type Id = String;
|
||||
|
||||
fn get_id(&self) -> Self::Id {
|
||||
self.rj_num.clone()
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: Self::Id) {
|
||||
self.rj_num = id;
|
||||
}
|
||||
|
||||
fn get_column_name() -> String {
|
||||
String::from("dl_games")
|
||||
}
|
||||
}
|
||||
|
||||
impl RocksReferences<DLSiteGenre> for DLSiteManiax {
|
||||
fn get_reference_ids(&self) -> Vec<<DLSiteGenre as RocksColumn>::Id> {
|
||||
self.genre_ids.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Text<'_>> for &DLSiteManiax {
|
||||
fn into(self) -> Text<'static> {
|
||||
Text::from(self.rj_num.to_string())
|
||||
}
|
||||
}
|
||||
39
models/src/dlsite/mod.rs
Executable file
39
models/src/dlsite/mod.rs
Executable file
@@ -0,0 +1,39 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
use color_eyre::Report;
|
||||
use language_tags::LanguageTag;
|
||||
|
||||
mod translation;
|
||||
mod category;
|
||||
mod genre;
|
||||
mod maniax;
|
||||
pub mod crawler;
|
||||
|
||||
pub use translation::{EN_LOCALE, JP_LOCALE, DLSiteTranslation};
|
||||
pub use category::DLSiteCategory;
|
||||
pub use genre::DLSiteGenre;
|
||||
pub use maniax::DLSiteManiax;
|
||||
|
||||
pub fn matches_primary_language(left: &LanguageTag, right: &LanguageTag) -> bool {
|
||||
left.primary_language() == right.primary_language()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PrimaryLanguage {
|
||||
EN, JP
|
||||
}
|
||||
|
||||
impl TryFrom<&LanguageTag> for PrimaryLanguage {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(value: &LanguageTag) -> Result<Self, Self::Error> {
|
||||
if matches_primary_language(&value, &EN_LOCALE) {
|
||||
Ok(PrimaryLanguage::EN)
|
||||
}
|
||||
else if matches_primary_language(&value, &JP_LOCALE) {
|
||||
Ok(PrimaryLanguage::JP)
|
||||
}
|
||||
else {
|
||||
Err(eyre!("No matching primary language found for {}", value))
|
||||
}
|
||||
}
|
||||
}
|
||||
56
models/src/dlsite/translation.rs
Executable file
56
models/src/dlsite/translation.rs
Executable file
@@ -0,0 +1,56 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
use color_eyre::Report;
|
||||
use language_tags::LanguageTag;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use lazy_static::lazy_static;
|
||||
use crate::config::ApplicationConfig;
|
||||
use super::matches_primary_language;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref EN_LOCALE: LanguageTag = LanguageTag::parse("en").unwrap();
|
||||
pub static ref JP_LOCALE: LanguageTag = LanguageTag::parse("ja").unwrap();
|
||||
pub static ref SUPPORTED_LOCALES: [LanguageTag; 2] = [JP_LOCALE.clone(), EN_LOCALE.clone()];
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum DLSiteTranslation {
|
||||
EN(String), JP(String)
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for DLSiteTranslation {
|
||||
type Error = Report;
|
||||
fn try_from(value: &str) -> color_eyre::Result<Self> {
|
||||
Self::try_from(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for DLSiteTranslation {
|
||||
type Error = Report;
|
||||
fn try_from(value: String) -> color_eyre::Result<Self> {
|
||||
let app_conf = ApplicationConfig::get_config()?;
|
||||
let locale = app_conf.basic_config.locale;
|
||||
|
||||
if matches_primary_language(&locale, &EN_LOCALE) {
|
||||
return Ok(DLSiteTranslation::EN(value));
|
||||
}
|
||||
if matches_primary_language(&locale, &JP_LOCALE) {
|
||||
return Ok(DLSiteTranslation::JP(value));
|
||||
}
|
||||
Err(eyre!(
|
||||
"Invalid Locale: {:?}; Support {:?}",
|
||||
locale,
|
||||
[EN_LOCALE.to_string(), JP_LOCALE.to_string()])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<String> for DLSiteTranslation {
|
||||
type Error = Report;
|
||||
|
||||
fn try_into(self) -> Result<String, Self::Error> {
|
||||
match self {
|
||||
DLSiteTranslation::EN(val) => Ok(val),
|
||||
DLSiteTranslation::JP(val) => Ok(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
24
models/src/lib.rs
Executable file
24
models/src/lib.rs
Executable file
@@ -0,0 +1,24 @@
|
||||
use std::hash::RandomState;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use dashmap::DashMap;
|
||||
use directories::BaseDirs;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
pub mod dlsite;
|
||||
pub mod config;
|
||||
|
||||
const APP_DIR_NAME: &str = "sus_manager";
|
||||
lazy_static! {
|
||||
static ref BASE_DIRS: BaseDirs = BaseDirs::new().unwrap();
|
||||
pub static ref APP_CONFIG_DIR: PathBuf =
|
||||
BASE_DIRS.config_dir().to_path_buf().join(APP_DIR_NAME);
|
||||
pub static ref APP_DATA_DIR: PathBuf = BASE_DIRS.data_dir().to_path_buf().join(APP_DIR_NAME);
|
||||
pub static ref APP_CACHE_PATH: PathBuf = BASE_DIRS.cache_dir().to_path_buf().join(APP_DIR_NAME);
|
||||
pub static ref APP_CONIFG_FILE_PATH: PathBuf = APP_CONFIG_DIR.clone().join("config.json");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CACHE_MAP: Arc<DashMap<String, serde_json::Value>> =
|
||||
Arc::new(DashMap::with_hasher(RandomState::default()));
|
||||
}
|
||||
Reference in New Issue
Block a user