Prepare Gotcha 1.0 for release
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use gotcha_gitea::Client;
|
||||
use std::sync::Mutex;
|
||||
use thiserror::Error;
|
||||
|
||||
mod api;
|
||||
mod core;
|
||||
mod domain;
|
||||
mod presentation;
|
||||
mod storage;
|
||||
@@ -65,766 +64,6 @@ pub struct GotchaCore {
|
||||
startup_error: Option<String>,
|
||||
}
|
||||
|
||||
#[uniffi::export(async_runtime = "tokio")]
|
||||
impl GotchaCore {
|
||||
#[uniffi::constructor]
|
||||
pub fn new() -> Arc<Self> {
|
||||
let (preferences, startup_error) = match load_preferences() {
|
||||
Ok(preferences) => (preferences, None),
|
||||
Err(error) => (Preferences::default(), Some(error)),
|
||||
};
|
||||
let active_server = preferences
|
||||
.last_server
|
||||
.filter(|index| *index < preferences.servers.len())
|
||||
.or_else(|| (preferences.servers.len() == 1).then_some(0));
|
||||
Arc::new(Self {
|
||||
state: Mutex::new(State {
|
||||
preferences,
|
||||
active_server,
|
||||
..Default::default()
|
||||
}),
|
||||
startup_error,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn startup_error(&self) -> Option<String> {
|
||||
self.startup_error.clone()
|
||||
}
|
||||
|
||||
pub fn servers(&self) -> Vec<ServerRow> {
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.preferences
|
||||
.servers
|
||||
.iter()
|
||||
.map(|server| ServerRow {
|
||||
name: server.name.clone(),
|
||||
url: server.url.clone(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn active_server_index(&self) -> Option<u32> {
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.active_server
|
||||
.map(|index| index as u32)
|
||||
}
|
||||
|
||||
pub fn active_server_name(&self) -> Option<String> {
|
||||
let state = self.state.lock().unwrap();
|
||||
state
|
||||
.active_server
|
||||
.and_then(|index| state.preferences.servers.get(index))
|
||||
.map(|server| server.name.clone())
|
||||
}
|
||||
|
||||
pub fn select_server(&self, index: u32) -> Result<(), GotchaError> {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let index = index as usize;
|
||||
if index >= state.preferences.servers.len() {
|
||||
return Err("That server no longer exists.".into());
|
||||
}
|
||||
state.active_server = Some(index);
|
||||
state.preferences.last_server = Some(index);
|
||||
state.repositories.clear();
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn add_server(
|
||||
&self,
|
||||
name: String,
|
||||
url: String,
|
||||
token: String,
|
||||
) -> Result<u32, GotchaError> {
|
||||
let server = validate_server(&name, &url, &token)?;
|
||||
Client::new(&server.url, Some(&server.token))
|
||||
.map_err(|error| error.to_string())?
|
||||
.current_user()
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
save_server_token(&server)?;
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.preferences.servers.push(server);
|
||||
let index = state.preferences.servers.len() - 1;
|
||||
state.preferences.last_server = Some(index);
|
||||
state.active_server = Some(index);
|
||||
save_preferences(&state.preferences)?;
|
||||
Ok(index as u32)
|
||||
}
|
||||
|
||||
pub fn settings(&self) -> Settings {
|
||||
let state = self.state.lock().unwrap();
|
||||
Settings {
|
||||
issue_status: state.preferences.issue_status.clone(),
|
||||
pull_status: state.preferences.pull_status.clone(),
|
||||
appearance: state.preferences.appearance.index() as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_issue_status(&self, status: String) -> Result<(), GotchaError> {
|
||||
if !matches!(status.as_str(), "open" | "closed") {
|
||||
return Err("Unsupported issue status.".into());
|
||||
}
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.preferences.issue_status = status;
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn set_pull_status(&self, status: String) -> Result<(), GotchaError> {
|
||||
if !matches!(status.as_str(), "open" | "closed") {
|
||||
return Err("Unsupported pull-request status.".into());
|
||||
}
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.preferences.pull_status = status;
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn set_appearance(&self, index: u32) -> Result<(), GotchaError> {
|
||||
let appearance =
|
||||
AppearanceMode::from_index(index as i32).ok_or("Unsupported appearance.")?;
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.preferences.appearance = appearance;
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn home(
|
||||
&self,
|
||||
page: u32,
|
||||
filter: HomeActivityFilter,
|
||||
) -> Result<HomePage, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let name = server.name.clone();
|
||||
Ok(home_page(
|
||||
name,
|
||||
load_home(&server, valid_page(page)?, filter.into()).await?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn repositories(
|
||||
&self,
|
||||
page: u32,
|
||||
pane: RepositoryPane,
|
||||
) -> Result<RepositoryListPage, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let page_number = valid_page(page)?;
|
||||
let repositories = load_repositories(&server, page_number).await?;
|
||||
let mut state = self.state.lock().unwrap();
|
||||
if page_number == 1 {
|
||||
state.repositories.clear();
|
||||
}
|
||||
for repository in repositories.items {
|
||||
if let Some(existing) = state.repositories.iter_mut().find(|candidate| {
|
||||
candidate.owner == repository.owner && candidate.name == repository.name
|
||||
}) {
|
||||
*existing = repository;
|
||||
} else {
|
||||
state.repositories.push(repository);
|
||||
}
|
||||
}
|
||||
Ok(RepositoryListPage {
|
||||
rows: self.repository_rows(&state, pane),
|
||||
has_more: repositories.has_more,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn toggle_favorite(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
pane: RepositoryPane,
|
||||
) -> Result<Vec<RepositoryRow>, GotchaError> {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let server = state
|
||||
.active_server
|
||||
.and_then(|index| state.preferences.servers.get(index))
|
||||
.ok_or("Select a server first.")?;
|
||||
let key = favorite_key(pane, &server.url, &owner, &repository);
|
||||
if !state.preferences.favorites.remove(&key) {
|
||||
state.preferences.favorites.insert(key);
|
||||
}
|
||||
save_preferences(&state.preferences)?;
|
||||
Ok(self.repository_rows(&state, pane))
|
||||
}
|
||||
|
||||
pub async fn issues(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
page: u32,
|
||||
) -> Result<IssueListPage, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let (status, filter) = {
|
||||
let state = self.state.lock().unwrap();
|
||||
(
|
||||
state.preferences.issue_status.clone(),
|
||||
state
|
||||
.preferences
|
||||
.issue_filters
|
||||
.get(&repository_key(&server.url, &owner, &repository))
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
};
|
||||
let page = load_issues(
|
||||
&server,
|
||||
&owner,
|
||||
&repository,
|
||||
&status,
|
||||
&filter,
|
||||
valid_page(page)?,
|
||||
)
|
||||
.await?;
|
||||
Ok(IssueListPage {
|
||||
rows: issue_rows(&page.items),
|
||||
has_more: page.has_more,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn issue_filters(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
) -> Result<IssueFilterOptions, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let filter = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.preferences
|
||||
.issue_filters
|
||||
.get(&repository_key(&server.url, &owner, &repository))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let (labels, milestones) = tokio::try_join!(
|
||||
load_labels(&server, &owner, &repository),
|
||||
load_milestones(&server, &owner, &repository)
|
||||
)?;
|
||||
Ok(issue_filter_options(&labels, &milestones, &filter))
|
||||
}
|
||||
|
||||
pub fn issue_filters_active(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
) -> Result<bool, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let state = self.state.lock().unwrap();
|
||||
let filter = state
|
||||
.preferences
|
||||
.issue_filters
|
||||
.get(&repository_key(
|
||||
&server.url,
|
||||
owner.trim(),
|
||||
repository.trim(),
|
||||
))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
Ok(filter.is_active(&state.preferences.issue_status))
|
||||
}
|
||||
|
||||
pub fn set_issue_filters(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
milestone: String,
|
||||
labels: Vec<String>,
|
||||
search_text: String,
|
||||
) -> Result<(), GotchaError> {
|
||||
let owner = owner.trim();
|
||||
let repository = repository.trim();
|
||||
if owner.is_empty() || repository.is_empty() {
|
||||
return Err("Select a repository first.".into());
|
||||
}
|
||||
let filter = IssueFilter {
|
||||
milestone,
|
||||
labels: labels
|
||||
.into_iter()
|
||||
.filter(|label| !label.is_empty())
|
||||
.collect(),
|
||||
search_text: search_text.trim().into(),
|
||||
};
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let server = state
|
||||
.active_server
|
||||
.and_then(|index| state.preferences.servers.get(index))
|
||||
.ok_or("Select a server first.")?;
|
||||
let key = repository_key(&server.url, owner, repository);
|
||||
if filter == IssueFilter::default() {
|
||||
state.preferences.issue_filters.remove(&key);
|
||||
} else {
|
||||
state.preferences.issue_filters.insert(key, filter);
|
||||
}
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn clear_issue_filters(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
) -> Result<(), GotchaError> {
|
||||
let owner = owner.trim();
|
||||
let repository = repository.trim();
|
||||
if owner.is_empty() || repository.is_empty() {
|
||||
return Err("Select a repository first.".into());
|
||||
}
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let server = state
|
||||
.active_server
|
||||
.and_then(|index| state.preferences.servers.get(index))
|
||||
.ok_or("Select a server first.")?;
|
||||
let key = repository_key(&server.url, owner, repository);
|
||||
state.preferences.issue_status = "open".into();
|
||||
state.preferences.issue_filters.remove(&key);
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn issue(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: i64,
|
||||
page: u32,
|
||||
) -> Result<IssuePage, GotchaError> {
|
||||
Ok(issue_page(
|
||||
load_issue(
|
||||
&self.server()?,
|
||||
&owner,
|
||||
&repository,
|
||||
number,
|
||||
valid_page(page)?,
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn save_issue_comment(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: i64,
|
||||
comment_id: Option<i64>,
|
||||
body: String,
|
||||
) -> Result<(), GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
if number <= 0 || comment_id.is_some_and(|id| id <= 0) {
|
||||
return Err("Invalid issue comment selection.".into());
|
||||
}
|
||||
if body.trim().is_empty() {
|
||||
return Err("Enter a comment.".into());
|
||||
}
|
||||
save_issue_comment(&self.server()?, owner, repository, number, comment_id, body).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn issue_editor(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: Option<i64>,
|
||||
) -> Result<IssueEditorPage, GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
if number.is_some_and(|number| number <= 0) {
|
||||
return Err("Invalid issue number.".into());
|
||||
}
|
||||
Ok(issue_editor_page(
|
||||
load_issue_editor(&self.server()?, owner, repository, number).await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn save_issue(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: Option<i64>,
|
||||
title: String,
|
||||
body: String,
|
||||
label_ids: Vec<i64>,
|
||||
milestone_id: Option<i64>,
|
||||
due_date: Option<i64>,
|
||||
closed: bool,
|
||||
) -> Result<i64, GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
let title = title.trim();
|
||||
if title.is_empty() {
|
||||
return Err("Enter an issue title.".into());
|
||||
}
|
||||
if number.is_some_and(|number| number <= 0)
|
||||
|| milestone_id.is_some_and(|id| id <= 0)
|
||||
|| label_ids.iter().any(|id| *id <= 0)
|
||||
{
|
||||
return Err("Invalid issue editor selection.".into());
|
||||
}
|
||||
let label_ids: Vec<_> = label_ids
|
||||
.into_iter()
|
||||
.collect::<std::collections::BTreeSet<_>>()
|
||||
.into_iter()
|
||||
.collect();
|
||||
let draft = IssueDraft {
|
||||
title: title.into(),
|
||||
body,
|
||||
label_ids,
|
||||
milestone_id,
|
||||
due_date,
|
||||
closed,
|
||||
};
|
||||
let server = self.server()?;
|
||||
let issue = match number {
|
||||
Some(number) => edit_issue(&server, owner, repository, number, draft).await?,
|
||||
None => create_issue(&server, owner, repository, draft).await?,
|
||||
};
|
||||
issue
|
||||
.number
|
||||
.ok_or_else(|| "The saved issue has no number.".into())
|
||||
}
|
||||
|
||||
pub async fn set_issue_closed(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: i64,
|
||||
closed: bool,
|
||||
) -> Result<(), GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
if number <= 0 {
|
||||
return Err("Invalid issue number.".into());
|
||||
}
|
||||
set_issue_closed(&self.server()?, owner, repository, number, closed).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_issue(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: i64,
|
||||
) -> Result<(), GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
if number <= 0 {
|
||||
return Err("Invalid issue number.".into());
|
||||
}
|
||||
delete_issue(&self.server()?, owner, repository, number).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn milestones(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
page: u32,
|
||||
) -> Result<MilestoneListPage, GotchaError> {
|
||||
let page =
|
||||
load_milestones_page(&self.server()?, &owner, &repository, valid_page(page)?).await?;
|
||||
Ok(MilestoneListPage {
|
||||
rows: milestone_rows(&page.items),
|
||||
has_more: page.has_more,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn milestone(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
id: i64,
|
||||
page: u32,
|
||||
) -> Result<MilestonePage, GotchaError> {
|
||||
Ok(milestone_page(
|
||||
load_milestone(&self.server()?, &owner, &repository, id, valid_page(page)?).await?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn milestone_editor(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
id: Option<i64>,
|
||||
) -> Result<MilestoneEditorPage, GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
if id.is_some_and(|id| id <= 0) {
|
||||
return Err("Invalid milestone selection.".into());
|
||||
}
|
||||
let milestone = match id {
|
||||
Some(id) => Some(load_milestone_editor(&self.server()?, owner, repository, id).await?),
|
||||
None => None,
|
||||
};
|
||||
Ok(milestone_editor_page(milestone))
|
||||
}
|
||||
|
||||
pub async fn save_milestone(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
id: Option<i64>,
|
||||
draft: MilestoneEditorPage,
|
||||
) -> Result<i64, GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
let title = draft.title.trim();
|
||||
if title.is_empty() {
|
||||
return Err("Enter a milestone title.".into());
|
||||
}
|
||||
if id.is_some_and(|id| id <= 0) {
|
||||
return Err("Invalid milestone selection.".into());
|
||||
}
|
||||
let milestone = save_milestone(
|
||||
&self.server()?,
|
||||
owner,
|
||||
repository,
|
||||
id,
|
||||
MilestoneDraft {
|
||||
title: title.into(),
|
||||
description: draft.description,
|
||||
due_date: draft.due_date,
|
||||
closed: draft.closed,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
milestone
|
||||
.id
|
||||
.ok_or_else(|| "The saved milestone has no ID.".into())
|
||||
}
|
||||
|
||||
pub async fn set_milestone_closed(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
id: i64,
|
||||
closed: bool,
|
||||
) -> Result<(), GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
if id <= 0 {
|
||||
return Err("Invalid milestone selection.".into());
|
||||
}
|
||||
set_milestone_closed(&self.server()?, owner, repository, id, closed).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_milestone(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
id: i64,
|
||||
) -> Result<(), GotchaError> {
|
||||
let (owner, repository) = validate_repository(&owner, &repository)?;
|
||||
if id <= 0 {
|
||||
return Err("Invalid milestone selection.".into());
|
||||
}
|
||||
delete_milestone(&self.server()?, owner, repository, id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn pull_filters(&self) -> Result<PullFilterOptions, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let filter = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.preferences
|
||||
.pull_filters
|
||||
.get(&server.url)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
Ok(pull_filter_options(
|
||||
&load_pull_milestones(&server).await?,
|
||||
&filter,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn pull_filters_active(&self) -> Result<bool, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let state = self.state.lock().unwrap();
|
||||
let filter = state
|
||||
.preferences
|
||||
.pull_filters
|
||||
.get(&server.url)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
Ok(filter.is_active(&state.preferences.pull_status))
|
||||
}
|
||||
|
||||
pub fn set_pull_filters(
|
||||
&self,
|
||||
milestone: String,
|
||||
search_text: String,
|
||||
) -> Result<(), GotchaError> {
|
||||
let filter = PullFilter {
|
||||
milestone,
|
||||
search_text: search_text.trim().into(),
|
||||
};
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let server = state
|
||||
.active_server
|
||||
.and_then(|index| state.preferences.servers.get(index))
|
||||
.ok_or("Select a server first.")?;
|
||||
let key = server.url.clone();
|
||||
if filter == PullFilter::default() {
|
||||
state.preferences.pull_filters.remove(&key);
|
||||
} else {
|
||||
state.preferences.pull_filters.insert(key, filter);
|
||||
}
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn clear_pull_filters(&self) -> Result<(), GotchaError> {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let server = state
|
||||
.active_server
|
||||
.and_then(|index| state.preferences.servers.get(index))
|
||||
.ok_or("Select a server first.")?;
|
||||
let key = server.url.clone();
|
||||
state.preferences.pull_status = "open".into();
|
||||
state.preferences.pull_filters.remove(&key);
|
||||
save_preferences(&state.preferences).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn pulls(&self, page: u32) -> Result<PullListPage, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let (status, filter) = {
|
||||
let state = self.state.lock().unwrap();
|
||||
(
|
||||
state.preferences.pull_status.clone(),
|
||||
state
|
||||
.preferences
|
||||
.pull_filters
|
||||
.get(&server.url)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
};
|
||||
let page = load_pulls(
|
||||
&server,
|
||||
&status,
|
||||
&filter.milestone,
|
||||
&filter.search_text,
|
||||
valid_page(page)?,
|
||||
)
|
||||
.await?;
|
||||
Ok(PullListPage {
|
||||
rows: pull_rows(&page.items),
|
||||
has_more: page.has_more,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn pull(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: i64,
|
||||
page: u32,
|
||||
) -> Result<PullPage, GotchaError> {
|
||||
Ok(pull_page(
|
||||
load_pull(
|
||||
&self.server()?,
|
||||
&owner,
|
||||
&repository,
|
||||
number,
|
||||
valid_page(page)?,
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn commits(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
branch: Option<String>,
|
||||
path: String,
|
||||
pages: u32,
|
||||
) -> Result<CommitPage, GotchaError> {
|
||||
let server = self.server()?;
|
||||
let default_branch = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.repositories
|
||||
.iter()
|
||||
.find(|candidate| candidate.owner == owner && candidate.name == repository)
|
||||
.map(|candidate| candidate.default_branch.clone())
|
||||
.unwrap_or_else(|| "main".into());
|
||||
let branches = load_branches(&server, &owner, &repository, &default_branch).await?;
|
||||
let path = (!path.is_empty()).then_some(path.as_str());
|
||||
let commits = match branch.as_deref() {
|
||||
Some(branch) => {
|
||||
load_branch_commits(&server, &owner, &repository, branch, path, pages.max(1))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
load_all_commits(&server, &owner, &repository, &branches, path, pages.max(1))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(commit_page(
|
||||
branches,
|
||||
&commits.items,
|
||||
branch.is_none(),
|
||||
commits.has_more,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn repository_contents(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
path: String,
|
||||
) -> Result<Vec<RepositoryContentRow>, GotchaError> {
|
||||
Ok(repository_content_rows(
|
||||
load_repository_contents(&self.server()?, &owner, &repository, &path).await?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn repository_file(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
path: String,
|
||||
) -> Result<RepositoryFilePage, GotchaError> {
|
||||
let data = load_repository_file(&self.server()?, &owner, &repository, &path).await?;
|
||||
Ok(repository_file_page(&path, data))
|
||||
}
|
||||
|
||||
pub async fn commit_details(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
sha: String,
|
||||
branch: Option<String>,
|
||||
) -> Result<CommitDetailsPage, GotchaError> {
|
||||
Ok(commit_details_page(
|
||||
load_commit(&self.server()?, &owner, &repository, &sha).await?,
|
||||
branch,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn commit_diff(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
sha: String,
|
||||
path: String,
|
||||
) -> Result<DiffPage, GotchaError> {
|
||||
Ok(diff_page(
|
||||
&path,
|
||||
load_commit_diff(&self.server()?, &owner, &repository, &sha, &path).await?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn pull_diff(
|
||||
&self,
|
||||
owner: String,
|
||||
repository: String,
|
||||
number: i64,
|
||||
path: String,
|
||||
) -> Result<DiffPage, GotchaError> {
|
||||
Ok(diff_page(
|
||||
&path,
|
||||
load_pull_diff(&self.server()?, &owner, &repository, number, &path).await?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_repository<'a>(
|
||||
owner: &'a str,
|
||||
repository: &'a str,
|
||||
|
||||
Reference in New Issue
Block a user