Add repository connections

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 69c7ab7ac8
commit 68ffdc323b

@ -1,9 +1,11 @@
use std::path::PathBuf; use std::path::PathBuf;
use rmp_ipc::IPCBuilder;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::context::Context; use crate::context::Context;
use crate::error::AppResult; use crate::error::{AppError, AppResult};
use tokio::fs; use tokio::fs;
use crate::settings::save_settings; use crate::settings::save_settings;
use rmp_ipc::context::Context as IPCContext;
static REPO_CONFIG_FILE: &str = "repo.toml"; static REPO_CONFIG_FILE: &str = "repo.toml";
@ -25,7 +27,7 @@ pub struct RepoConfig {
pub async fn get_repositories(context: tauri::State<'_, Context>) -> AppResult<Vec<Repository>> { pub async fn get_repositories(context: tauri::State<'_, Context>) -> AppResult<Vec<Repository>> {
let settings = context.settings.read().await; let settings = context.settings.read().await;
Ok(settings.repositories.clone()) Ok(settings.repositories.values().cloned().collect())
} }
#[tauri::command] #[tauri::command]
@ -43,17 +45,35 @@ pub async fn add_repository(name: String, path: String, context: tauri::State<'_
let mut repositories = Vec::new(); let mut repositories = Vec::new();
{ {
let mut settings = context.settings.write().await; let mut settings = context.settings.write().await;
settings.repositories.push(repo); settings.repositories.insert(repo.name.clone(), repo);
save_settings(&settings)?; save_settings(&settings)?;
repositories.append(&mut settings.repositories.clone()); repositories.append(&mut settings.repositories.values().cloned().collect());
} }
Ok(repositories) Ok(repositories)
} }
#[tauri::command]
pub async fn select_repository(name: String, context: tauri::State<'_, Context>) -> AppResult<()> {
let settings = context.settings.read().await;
let repo = settings.repositories.get(&name).ok_or(AppError::new(format!("Repository '{}' not found", name)))?;
let ipc = connect(&repo.address).await?;
let mut ipc_ctx = context.ipc.write().await;
*ipc_ctx = Some(ipc);
Ok(())
}
async fn read_repo_config(path: PathBuf) -> AppResult<RepoConfig> { async fn read_repo_config(path: PathBuf) -> AppResult<RepoConfig> {
let toml_str = fs::read_to_string(path).await?; let toml_str = fs::read_to_string(path).await?;
let config = toml::from_str(&toml_str)?; let config = toml::from_str(&toml_str)?;
Ok(config) Ok(config)
} }
/// Connects to the IPC Server
async fn connect(address: &str) -> AppResult<IPCContext> {
let ctx = IPCBuilder::new().address(address).build_client().await?;
Ok(ctx)
}

@ -1,20 +1,20 @@
use std::sync::Arc; use std::sync::Arc;
use rmp_ipc::client::IPCClient;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use rmp_ipc::context::Context as IPCContext;
use crate::commands::repo::Repository; use crate::commands::repo::Repository;
use crate::settings::Settings; use crate::settings::Settings;
#[derive(Clone)] #[derive(Clone)]
pub struct Context { pub struct Context {
pub active_repository: Option<Repository>, pub active_repository: Option<Repository>,
pub client: Option<Arc<IPCClient>>, pub ipc: Arc<RwLock<Option<IPCContext>>>,
pub settings: Arc<RwLock<Settings>> pub settings: Arc<RwLock<Settings>>
} }
impl Context { impl Context {
pub fn new(settings: Settings) -> Self { pub fn new(settings: Settings) -> Self {
Self { Self {
client: None, ipc: Arc::new(RwLock::new(None)),
active_repository: None, active_repository: None,
settings: Arc::new(RwLock::new(settings)) settings: Arc::new(RwLock::new(settings))
} }

@ -1,27 +1,50 @@
use std::io::Error; use std::error::Error;
use std::fmt::{Display, Formatter};
use serde::Serialize; use serde::Serialize;
pub type AppResult<T> = Result<T, AppError>; pub type AppResult<T> = Result<T, AppError>;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub enum AppError { pub struct AppError {
Msg(String) message: String
} }
impl AppError {
pub fn new<S: ToString>(msg: S) -> Self {
Self {
message: msg.to_string()
}
}
}
impl Display for AppError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.message.fmt(f)
}
}
impl Error for AppError {}
impl From<std::io::Error> for AppError { impl From<std::io::Error> for AppError {
fn from(e: Error) -> Self { fn from(e: std::io::Error) -> Self {
Self::Msg(e.to_string()) Self::new(e)
} }
} }
impl From<toml::de::Error> for AppError { impl From<toml::de::Error> for AppError {
fn from(e: toml::de::Error) -> Self { fn from(e: toml::de::Error) -> Self {
Self::Msg(format!("Failed to deserialize toml: {:?}", e)) Self::new(format!("Failed to deserialize toml: {:?}", e))
} }
} }
impl From<toml::ser::Error> for AppError { impl From<toml::ser::Error> for AppError {
fn from(e: toml::ser::Error) -> Self { fn from(e: toml::ser::Error) -> Self {
Self::Msg(format!("Failed to serialize to toml: {:?}", e)) Self::new(format!("Failed to serialize to toml: {:?}", e))
}
}
impl From<rmp_ipc::error::Error> for AppError {
fn from(e: rmp_ipc::error::Error) -> Self {
Self::new(format!("Daemon Error: {:?}", e))
} }
} }

@ -3,7 +3,7 @@
windows_subsystem = "windows" windows_subsystem = "windows"
)] )]
use crate::commands::repo::{get_repositories, add_repository}; use crate::commands::repo::{get_repositories, add_repository, select_repository};
use crate::context::Context; use crate::context::Context;
use crate::settings::load_settings; use crate::settings::load_settings;
@ -18,7 +18,7 @@ fn main() {
tauri::Builder::default() tauri::Builder::default()
.manage(context) .manage(context)
.invoke_handler(tauri::generate_handler![get_repositories, add_repository]) .invoke_handler(tauri::generate_handler![get_repositories, add_repository, select_repository])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");
} }

@ -1,3 +1,4 @@
use std::collections::HashMap;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::commands::repo::Repository; use crate::commands::repo::Repository;
use crate::error::AppResult; use crate::error::AppResult;
@ -9,7 +10,7 @@ static SETTINGS_FILE: &str = "settings.toml";
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
pub struct Settings { pub struct Settings {
pub repositories: Vec<Repository>, pub repositories: HashMap<String, Repository>,
} }
fn get_settings_path() -> PathBuf { fn get_settings_path() -> PathBuf {

@ -12,7 +12,8 @@
} }
.warn { .warn {
color: mat.get-color-from-palette($warn-palette); background-color: mat.get-color-from-palette($warn-palette);
color: white
} }
} }

@ -1,34 +1,42 @@
import { Component } from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Router} from "@angular/router"; import {Router} from "@angular/router";
import {RepositoryService} from "./services/repository/repository.service"; import {RepositoryService} from "./services/repository/repository.service";
import {DataloaderService} from "./services/dataloader/dataloader.service"; import {DataloaderService} from "./services/dataloader/dataloader.service";
import {MatSnackBar} from "@angular/material/snack-bar"; import {MatSnackBar} from "@angular/material/snack-bar";
import {ErrorBrokerService} from "./services/error-broker/error-broker.service";
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent { export class AppComponent implements OnInit{
title = 'mediarepo-ui'; title = 'mediarepo-ui';
constructor( constructor(
private router: Router, private router: Router,
private snackBar: MatSnackBar, private snackBar: MatSnackBar,
private errorBroker: ErrorBrokerService,
private dataloaderService: DataloaderService, private dataloaderService: DataloaderService,
private repoService: RepositoryService private repoService: RepositoryService,
) { ) {
} }
async ngOnInit() { async ngOnInit() {
this.dataloaderService.loaderError.subscribe({ this.errorBroker.errorCb = (err: { message: string }) => {
error: (err) => { console.error(err);
this.snackBar.open(err, undefined, {panelClass: "warn"}) this.showError(err)
} };
})
await this.dataloaderService.loadData(); await this.dataloaderService.loadData();
if (this.repoService.selectedRepository.getValue() == undefined) { if (this.repoService.selectedRepository.getValue() == undefined) {
await this.router.navigate(["repositories"]) await this.router.navigate(["repositories"])
} }
} }
private showError(err: { message: string }) {
this.snackBar.open(err.message, undefined, {
panelClass: "warn",
duration: 2000,
});
}
} }

@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms"; import {FormControl, FormGroup, Validators} from "@angular/forms";
import {RepositoryService} from "../../../services/repository/repository.service"; import {RepositoryService} from "../../../services/repository/repository.service";
import {MatSnackBar} from "@angular/material/snack-bar"; import {MatSnackBar} from "@angular/material/snack-bar";
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
@Component({ @Component({
selector: 'app-repo-form', selector: 'app-repo-form',
@ -16,7 +17,7 @@ export class RepoFormComponent implements OnInit {
}) })
constructor(private repoService: RepositoryService, private snackBar: MatSnackBar) { } constructor(private repoService: RepositoryService, private errorBroker: ErrorBrokerService) { }
ngOnInit(): void { ngOnInit(): void {
} }
@ -26,9 +27,7 @@ export class RepoFormComponent implements OnInit {
try { try {
await this.repoService.addRepository(name, path); await this.repoService.addRepository(name, path);
} catch(err) { } catch(err) {
this.snackBar.open(err.Msg, undefined, { this.errorBroker.showError(err);
panelClass: "warn"
})
} }
} }
} }

@ -2,6 +2,8 @@ import {Component, Input, OnInit} from '@angular/core';
import {Repository} from "../../../models/Repository"; import {Repository} from "../../../models/Repository";
import {RepositoryService} from "../../../services/repository/repository.service"; import {RepositoryService} from "../../../services/repository/repository.service";
import {Router} from "@angular/router"; import {Router} from "@angular/router";
import {MatSnackBar} from "@angular/material/snack-bar";
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
@Component({ @Component({
selector: 'app-repository-card', selector: 'app-repository-card',
@ -12,15 +14,19 @@ export class RepositoryCardComponent implements OnInit {
@Input() repository?: Repository; @Input() repository?: Repository;
constructor(private repoService: RepositoryService, private router: Router) {} constructor(private repoService: RepositoryService, private router: Router, private errorBroker: ErrorBrokerService) {}
ngOnInit(): void { ngOnInit(): void {
} }
async selectRepository() { async selectRepository() {
if (this.repository) { if (this.repository) {
this.repoService.setRepository(this.repository); try {
await this.router.navigate([""]); await this.repoService.setRepository(this.repository);
await this.router.navigate([""]);
} catch(err) {
this.errorBroker.showError(err);
}
} }
} }
} }

@ -1,22 +1,20 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import {RepositoryService} from "../repository/repository.service"; import {RepositoryService} from "../repository/repository.service";
import {BehaviorSubject} from "rxjs"; import {BehaviorSubject} from "rxjs";
import {ErrorBrokerService} from "../error-broker/error-broker.service";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class DataloaderService { export class DataloaderService {
loaderError = new BehaviorSubject(undefined); constructor(private erroBroker: ErrorBrokerService, private repositoryService: RepositoryService) { }
constructor(private repositoryService: RepositoryService) { }
public async loadData() { public async loadData() {
try { try {
await this.repositoryService.loadRepositories(); await this.repositoryService.loadRepositories();
} catch (err) { } catch (err) {
this.loaderError.error(err); this.erroBroker.showError(err);
console.error(err);
} }
} }
} }

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ErrorBrokerService } from './error-broker.service';
describe('ErrorBrokerService', () => {
let service: ErrorBrokerService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ErrorBrokerService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
import {BehaviorSubject} from "rxjs";
@Injectable({
providedIn: 'root'
})
export class ErrorBrokerService {
errorCb: Function | undefined;
constructor() { }
showError(error: {message: String}) {
if (this.errorCb) {
if (!error.message) {
this.errorCb({message: error});
} else {
this.errorCb({...error});
}
}
}
}

@ -17,7 +17,8 @@ export class RepositoryService {
this.repositories.next(repos); this.repositories.next(repos);
} }
public setRepository(repo: Repository) { public async setRepository(repo: Repository) {
await invoke("select_repository", {name: repo.name});
this.selectedRepository.next(repo); this.selectedRepository.next(repo);
} }

Loading…
Cancel
Save