ada-judge-app 0.2.2

Native desktop app for ada-judge
use crate::{
    AJAlertType, AJContestState, AJMainWindow, AJSubmission, alert::show_alert,
    app_state::AppState, load_all,
};
use ada_judge_public_models::{
    testing::{Language, Submission, SubmissonRequest},
    users::PublicUserData,
};
use futures::future::try_join_all;
use reqwest::{
    StatusCode,
    multipart::{Form, Part},
};
use slint::{ComponentHandle, ModelRc, ToSharedString, VecModel, Weak};
use std::{fs, sync::Arc};
use tokio::sync::Mutex;

pub async fn load_problem_submissions(
    problem_id: i64,
    all_submissions: bool,
    state: Arc<Mutex<AppState>>,
    weak: Weak<AJMainWindow>,
) -> Result<(), anyhow::Error> {
    let client = reqwest::Client::new();
    let token = state.lock().await.token.clone();
    let base_url = state.lock().await.base_url.clone();
    let ids = if all_submissions {
        client
            .get(format!(
                "{base_url}/submissions/filter/problem/{problem_id}"
            ))
            .bearer_auth(token.clone())
            .send()
            .await?
            .json::<Vec<i64>>()
            .await?
    } else {
        client
            .get(format!(
                "{base_url}/submissions/my/filter/problem/{problem_id}"
            ))
            .bearer_auth(token.clone())
            .send()
            .await?
            .json::<Vec<i64>>()
            .await?
    };
    let submissions: Vec<Submission> = try_join_all(ids.iter().map(|submission_id| {
        let client = client.clone();
        let token = token.clone();
        let base_url = base_url.clone();

        async move {
            client
                .get(format!("{base_url}/submissions/{submission_id}"))
                .bearer_auth(token.clone())
                .send()
                .await?
                .json::<Submission>()
                .await
        }
    }))
    .await?;
    state.lock().await.submissions = submissions.clone();
    let submissions: Vec<(String, Submission)> = try_join_all(submissions.iter().map(|sbm| {
        let client = client.clone();
        let token = token.clone();
        let base_url = base_url.clone();

        async move {
            let user = client
                .get(format!("{base_url}/users/{}", sbm.user_id))
                .bearer_auth(token.clone())
                .send()
                .await?
                .json::<PublicUserData>()
                .await?;
            Ok::<_, anyhow::Error>((user.login, sbm.clone()))
        }
    }))
    .await?;
    slint::invoke_from_event_loop(move || {
        let submissions: Vec<AJSubmission> = submissions
            .iter()
            .map(|(username, sbm)| (username.clone(), sbm).into())
            .collect();

        if let Some(ui) = weak.upgrade() {
            ui.global::<AJContestState>()
                .set_submissions(ModelRc::new(VecModel::from(submissions)));
        }
    })
    .unwrap();

    Ok(())
}

pub fn submit(
    contest_id: i64,
    problem_id: i64,
    language: Language,
    state: Arc<Mutex<AppState>>,
    weak: Weak<AJMainWindow>,
) {
    tokio::spawn(async move {
        if let Err(e) = submit_impl(
            contest_id,
            problem_id,
            language,
            state.clone(),
            weak.clone(),
        )
        .await
        {
            show_alert(weak.clone(), e, AJAlertType::Error);
        }
    });
}

pub async fn submit_impl(
    contest_id: i64,
    problem_id: i64,
    language: Language,
    state: Arc<Mutex<AppState>>,
    weak: Weak<AJMainWindow>,
) -> Result<(), anyhow::Error> {
    let client = reqwest::Client::new();
    let token = state.lock().await.token.clone();
    let base_url = state.lock().await.base_url.clone();
    let request = SubmissonRequest {
        problem_id,
        language,
    };
    let json = serde_json::to_string(&request)?;
    let file = fs::read(state.lock().await.solution_file.clone())?;
    let form = Form::new()
        .part("submission_data", Part::text(json))
        .part("submission_file", Part::bytes(file));
    let res = client
        .post(format!("{base_url}/contests/{contest_id}/submit"))
        .bearer_auth(token.clone())
        .multipart(form)
        .send()
        .await?;

    load_all(state.clone(), weak.clone()).await;

    match res.status() {
        StatusCode::OK => {
            show_alert(
                weak.clone(),
                "успешно".to_shared_string(),
                AJAlertType::Success,
            );
        }
        _ => {
            show_alert(
                weak.clone(),
                "ошибка".to_shared_string(),
                AJAlertType::Error,
            );
        }
    }

    Ok(())
}