chore: Starting migrating references to database applications, auth, and base_url

Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
quexeky
2025-10-22 18:55:55 +11:00
parent a2d1a989e0
commit 50eb7ff716
43 changed files with 968 additions and 623 deletions
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "library"
version = "0.1.0"
edition = "2024"
[dependencies]
async-trait = "0.1.89"
client = { version = "0.1.0", path = "../client" }
database = { version = "0.1.0", path = "../database" }
futures = "0.3.31"
itertools = "0.14.0"
log = "0.4.28"
remote = { version = "0.1.0", path = "../remote" }
serde = { version = "1.0.228", features = ["derive"] }
serde_with = "3.15.0"
tauri = "2.8.5"
url = "2.5.7"
+134
View File
@@ -0,0 +1,134 @@
use std::sync::nonpoison::Mutex;
use async_trait::async_trait;
use client::app_state::AppState;
use database::{
DatabaseAuth, GameDownloadStatus, borrow_db_mut_checked,
models::{Collection, Game, LibraryProviderMetadata},
};
use log::warn;
use remote::{
auth::generate_authorization_header,
cache::{cache_object, get_cached_object, get_cached_object_db},
error::{DropServerError, RemoteAccessError},
requests::generate_url,
utils::DROP_CLIENT_ASYNC,
};
use url::Url;
use crate::{error::LibraryError, provider::LibraryProvider};
pub struct DropLibraryProvider {
metadata: LibraryProviderMetadata,
auth: DatabaseAuth,
base_url: Url,
}
impl DropLibraryProvider {
pub fn new(metadata: LibraryProviderMetadata, auth: DatabaseAuth, base_url: Url) -> Self {
Self {
metadata,
auth,
base_url,
}
}
}
#[async_trait]
impl LibraryProvider for DropLibraryProvider {
async fn get_library(
&self,
state: &tauri::State<'_, Mutex<AppState>>,
) -> Result<Vec<Game>, LibraryError> {
// let do_hard_refresh = hard_fresh.unwrap_or(false);
if
/* !do_hard_refresh &&*/
let Ok(library) = get_cached_object("library") {
return Ok(library);
}
let client = DROP_CLIENT_ASYNC.clone();
let response = generate_url(&["/api/v1/client/user/library"], &[], self.base_url)?;
let response = client
.get(response)
.header("Authorization", generate_authorization_header(self.auth))
.send()
.await
.map_err(|e| LibraryError::FetchError(RemoteAccessError::FetchError(e.into())))?;
if response.status() != 200 {
let err = response.json().await.unwrap_or(DropServerError {
status_code: 500,
status_message: "Invalid response from server.".to_owned(),
});
warn!("{err:?}");
return Err(LibraryError::FetchError(
RemoteAccessError::InvalidResponse(err),
));
}
let mut games: Vec<Game> = response
.json()
.await
.map_err(|e| RemoteAccessError::FetchError(e.into()))?;
let mut handle = state.lock();
let mut db_handle = borrow_db_mut_checked();
for game in &games {
handle.games_mut().insert(game.id().clone(), game.clone());
if !db_handle.applications.game_statuses.contains_key(game.id()) {
db_handle
.applications
.game_statuses
.insert(game.id().clone(), GameDownloadStatus::Remote {});
}
}
// Add games that are installed but no longer in library
for meta in db_handle.applications.installed_game_version.values() {
if games.iter().any(|e| *e.id() == meta.id) {
continue;
}
// We should always have a cache of the object
// Pass db_handle because otherwise we get a gridlock
let game = match get_cached_object_db::<Game>(&meta.id.clone(), &db_handle) {
Ok(game) => game,
Err(err) => {
warn!(
"{} is installed, but encountered error fetching its error: {}.",
meta.id, err
);
continue;
}
};
games.push(game);
}
drop(handle);
drop(db_handle);
cache_object("library", &games)?;
Ok(games)
}
async fn get_collections(&self) -> Result<Vec<Collection>, LibraryError> {
todo!()
}
fn install(&mut self, game_id: String) {
todo!()
}
fn uninstall(&mut self, game_id: String) {
todo!()
}
fn metadata(&self) -> LibraryProviderMetadata {
todo!()
}
}
async fn fetch_library_logic(state: &Mutex<AppState>) -> Result<Vec<Game>, RemoteAccessError> {}
async fn fetch_library_logic_offline() -> Result<Vec<Game>, RemoteAccessError> {
todo!()
}
+1
View File
@@ -0,0 +1 @@
pub mod drop;
+31
View File
@@ -0,0 +1,31 @@
use std::fmt::Display;
use database::models::LibraryProviderMetadata;
use remote::error::RemoteAccessError;
use serde_with::SerializeDisplay;
#[derive(Debug, SerializeDisplay)]
pub enum LibraryError {
ProviderConnection(ProviderError),
FetchError(RemoteAccessError)
}
#[derive(Debug, SerializeDisplay)]
pub struct ProviderError {
provider: LibraryProviderMetadata
}
impl Display for LibraryError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
impl Display for ProviderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
impl From<RemoteAccessError> for LibraryError {
fn from(value: RemoteAccessError) -> Self {
LibraryError::FetchError(value)
}
}
+69
View File
@@ -0,0 +1,69 @@
#![feature(nonpoison_mutex)]
#![feature(sync_nonpoison)]
use std::sync::{LazyLock, nonpoison::Mutex};
use client::app_state::AppState;
use database::{borrow_db_checked, models::{Game, LibraryProviderMetadata, ProviderType}};
use futures::{StreamExt, future::join_all};
use itertools::Itertools;
use crate::{drop::drop::DropLibraryProvider, error::LibraryError, provider::LibraryProvider};
pub mod drop;
pub mod error;
pub mod provider;
pub static LIBRARY: LazyLock<Library> = LazyLock::new(Library::init);
pub struct Library {
providers: Vec<Box<dyn LibraryProvider>>,
}
impl Library {
pub fn init() -> Self {
let metadata = borrow_db_checked();
let library = &metadata.library;
let providers = library.providers().iter().map(|provider| {
Library::construct(provider)
}).collect();
Self {
providers
}
}
fn construct(provider: &LibraryProviderMetadata) -> Box<dyn LibraryProvider> {
todo!()
}
pub async fn get_library(
&self,
state: &tauri::State<'_, Mutex<AppState>>,
) -> (Vec<Game>, Vec<LibraryError>) {
let res = join_all(
self.providers
.iter()
.map(|provider| provider.get_library(state)),
)
.await
.into_iter()
.fold(
(Vec::new(), Vec::new()),
|(mut acc_ok, mut acc_err), res| {
match res {
Ok(games) => acc_ok.extend(games),
Err(e) => acc_err.push(e),
};
(acc_ok, acc_err)
},
);
res
}
pub fn add(&mut self, provider: LibraryProviderMetadata) {
let new_provider = Box::new(match provider.provider() {
ProviderType::Drop(_) => DropLibraryProvider::new(provider),
});
self.providers.push(new_provider);
}
pub fn remove(&mut self, id: usize) {
self.providers.retain(|v| v.metadata().id() != id);
}
}
+16
View File
@@ -0,0 +1,16 @@
use std::sync::nonpoison::Mutex;
use async_trait::async_trait;
use client::app_state::AppState;
use database::models::{Collection, Game, LibraryProviderMetadata};
use crate::error::LibraryError;
#[async_trait]
pub trait LibraryProvider: Sync + Send {
async fn get_library(&self, state: &tauri::State<'_, Mutex<AppState>>) -> Result<Vec<Game>, LibraryError>;
async fn get_collections(&self) -> Result<Vec<Collection>, LibraryError>;
fn install(&mut self, game_id: String);
fn uninstall(&mut self, game_id: String);
fn metadata(&self) -> LibraryProviderMetadata;
}