1use anyhow::anyhow;
2use inquire::Password;
3
4use crate::app::cli::Cli;
5use crate::auth::AuthProvider;
6use crate::errors::AppError;
7use crate::github::GithubClient;
8use crate::logging::LoggingConfig;
9use crate::{logging, ui};
10use std::sync::OnceLock;
11
12pub struct App {
13 pub owner: String,
14 pub repo: String,
15}
16
17pub static GITHUB_CLIENT: OnceLock<GithubClient> = OnceLock::new();
18
19impl App {
20 pub async fn new(cli: Cli) -> Result<Self, AppError> {
21 logging::init(LoggingConfig::new(cli.args.log_level))?;
22 let auth = crate::auth::keyring::KeyringAuth::new("gitv")?;
23 let token = match auth.get_token().ok() {
24 Some(token) => token,
25 None => Self::handle_no_token(&auth)?,
26 };
27 let github = GithubClient::new(Some(token))?;
28 let _ = GITHUB_CLIENT.set(github);
29 Ok(Self {
30 owner: cli.args.owner.unwrap_or_default(),
31 repo: cli.args.repo.unwrap_or_default(),
32 })
33 }
34
35 pub async fn run(&mut self) -> Result<(), AppError> {
36 use crate::ui::AppState;
37 let current_user = GITHUB_CLIENT
38 .get()
39 .ok_or_else(|| AppError::Other(anyhow!("github client is not initialized")))?
40 .inner()
41 .current()
42 .user()
43 .await?
44 .login;
45
46 let ap = AppState::new(self.repo.clone(), self.owner.clone(), current_user);
47 ui::run(ap).await
48 }
49
50 pub fn handle_no_token(auth: &impl AuthProvider) -> Result<String, AppError> {
51 let prompt = Password::new("No token found. Please enter your github token")
52 .with_display_toggle_enabled()
53 .without_confirmation()
54 .with_display_mode(inquire::PasswordDisplayMode::Masked);
55 let token = prompt.prompt()?;
56 auth.set_token(&token)?;
57 Ok(token)
58 }
59}
60
61pub mod cli;