156 lines
3.8 KiB
Rust
156 lines
3.8 KiB
Rust
use std::sync::Mutex;
|
|
use thiserror::Error;
|
|
|
|
mod api;
|
|
mod core;
|
|
mod domain;
|
|
mod presentation;
|
|
mod storage;
|
|
|
|
use api::*;
|
|
use domain::*;
|
|
pub use presentation::*;
|
|
use storage::*;
|
|
|
|
uniffi::setup_scaffolding!();
|
|
|
|
#[derive(Clone, Copy, Debug, uniffi::Enum)]
|
|
pub enum RepositoryPane {
|
|
Issues,
|
|
Commits,
|
|
Milestones,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, uniffi::Enum)]
|
|
pub enum ServerProvider {
|
|
Gitea,
|
|
Forgejo,
|
|
}
|
|
|
|
impl From<ServerProvider> for gotcha_gitea::Provider {
|
|
fn from(provider: ServerProvider) -> Self {
|
|
match provider {
|
|
ServerProvider::Gitea => Self::Gitea,
|
|
ServerProvider::Forgejo => Self::Forgejo,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<gotcha_gitea::Provider> for ServerProvider {
|
|
fn from(provider: gotcha_gitea::Provider) -> Self {
|
|
match provider {
|
|
gotcha_gitea::Provider::Gitea => Self::Gitea,
|
|
gotcha_gitea::Provider::Forgejo => Self::Forgejo,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl RepositoryPane {
|
|
const ALL: [Self; 3] = [Self::Issues, Self::Commits, Self::Milestones];
|
|
|
|
const fn key(self) -> &'static str {
|
|
match self {
|
|
Self::Issues => "issues",
|
|
Self::Commits => "commits",
|
|
Self::Milestones => "milestones",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Error, uniffi::Error)]
|
|
pub enum GotchaError {
|
|
#[error("{message}")]
|
|
Message { message: String },
|
|
}
|
|
|
|
impl From<String> for GotchaError {
|
|
fn from(message: String) -> Self {
|
|
Self::Message { message }
|
|
}
|
|
}
|
|
|
|
impl From<&str> for GotchaError {
|
|
fn from(message: &str) -> Self {
|
|
message.to_string().into()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, uniffi::Record)]
|
|
pub struct Settings {
|
|
pub issue_status: String,
|
|
pub pull_status: String,
|
|
pub appearance: u32,
|
|
pub notifications_enabled: bool,
|
|
}
|
|
|
|
#[derive(uniffi::Object)]
|
|
pub struct GotchaCore {
|
|
state: Mutex<State>,
|
|
server_activity: tokio::sync::Mutex<Option<ServerActivitySession>>,
|
|
startup_error: Option<String>,
|
|
}
|
|
|
|
struct ServerActivitySession {
|
|
server_id: String,
|
|
next_page: u32,
|
|
pager: gotcha_gitea::ServerActivityPager,
|
|
}
|
|
|
|
fn validate_repository<'a>(
|
|
owner: &'a str,
|
|
repository: &'a str,
|
|
) -> Result<(&'a str, &'a str), GotchaError> {
|
|
let owner = owner.trim();
|
|
let repository = repository.trim();
|
|
if owner.is_empty() || repository.is_empty() {
|
|
return Err("Select a repository first.".into());
|
|
}
|
|
Ok((owner, repository))
|
|
}
|
|
|
|
fn valid_page(page: u32) -> Result<i32, GotchaError> {
|
|
(page > 0)
|
|
.then(|| i32::try_from(page).ok())
|
|
.flatten()
|
|
.ok_or_else(|| "Invalid page number.".into())
|
|
}
|
|
|
|
impl GotchaCore {
|
|
fn server(&self) -> Result<Server, GotchaError> {
|
|
let state = self.state.lock().unwrap();
|
|
state
|
|
.active_server
|
|
.and_then(|index| state.preferences.servers.get(index))
|
|
.cloned()
|
|
.ok_or_else(|| "Select a server first.".to_string().into())
|
|
}
|
|
|
|
fn server_by_id(&self, id: &str) -> Result<Server, GotchaError> {
|
|
self.state
|
|
.lock()
|
|
.unwrap()
|
|
.preferences
|
|
.servers
|
|
.iter()
|
|
.find(|server| server.credential_account == id)
|
|
.cloned()
|
|
.ok_or_else(|| "That server is no longer configured.".into())
|
|
}
|
|
|
|
fn repository_rows(&self, state: &State, pane: RepositoryPane) -> Vec<RepositoryRow> {
|
|
let server_url = state
|
|
.active_server
|
|
.and_then(|index| state.preferences.servers.get(index))
|
|
.map(|server| server.url.as_str())
|
|
.unwrap_or_default();
|
|
repository_rows(&state.repositories, |repository| {
|
|
state.preferences.favorites.contains(&favorite_key(
|
|
pane,
|
|
server_url,
|
|
&repository.owner,
|
|
&repository.name,
|
|
))
|
|
})
|
|
}
|
|
}
|