Skip to content

Commit

Permalink
Moves challenge info fetcher to background process
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbrain-za committed Dec 10, 2023
1 parent 49e04f0 commit 198eacd
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 108 deletions.
60 changes: 10 additions & 50 deletions src/apps/challenge_info.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use crate::helpers::{
fetchers::{RequestStatus, Requestor},
AppState, ChallengeCollection, Challenges,
};
use crate::helpers::{AppState, Challenges};
use egui_commonmark::*;
use std::borrow::BorrowMut;
use std::sync::{Arc, Mutex};

#[derive(PartialEq, Clone, Copy, serde::Deserialize, serde::Serialize)]
Expand All @@ -18,55 +14,18 @@ pub struct ChallengeInfoApp {
selected_challenge: Challenges,
#[serde(skip)]
active_challenge: Challenges,
#[serde(skip)]
info_fetcher: Option<Requestor>,
instructions: String,
#[serde(skip)]
app_state: Arc<Mutex<AppState>>,
#[serde(skip)]
challenges: ChallengeCollection,
}

impl Default for ChallengeInfoApp {
fn default() -> Self {
Self {
selected_challenge: Challenges::default(),
info_fetcher: None,
active_challenge: Challenges::None,
instructions: "None".to_string(),
app_state: Arc::new(Mutex::new(AppState::default())),
challenges: ChallengeCollection::default(),
}
}
}

impl ChallengeInfoApp {
fn fetch(&mut self) {
if self.active_challenge == self.selected_challenge {
return;
}
log::debug!("Fetching challenge info");
self.active_challenge = self.selected_challenge;
let app_state = Arc::clone(&self.app_state);
self.info_fetcher = self.challenges.fetch(app_state);
}
fn check_info_promise(&mut self) {
let getter = &mut self.info_fetcher;

if let Some(getter) = getter {
let result = &getter.check_promise();
match result {
RequestStatus::NotStarted => {}
RequestStatus::InProgress => {}
RequestStatus::Success(_) => {
self.info_fetcher = None;
}
RequestStatus::Failed(_) => {
self.info_fetcher = None;
}
}
self.challenges = ChallengeCollection::from_json(&result.to_string());
self.instructions = self.challenges.get_instructions(self.selected_challenge);
}
}
}
Expand All @@ -81,13 +40,15 @@ impl super::App for ChallengeInfoApp {
}

fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
self.fetch();

if let Some(fetcher) = self.info_fetcher.borrow_mut() {
if fetcher.refresh_context() {
log::debug!("Refreshing context");
ctx.request_repaint();
}
if self.active_challenge != self.selected_challenge {
self.active_challenge = self.selected_challenge;
self.instructions = self
.app_state
.lock()
.unwrap()
.challenges
.get_instructions(self.selected_challenge)
.unwrap_or("Unable to load instructions".to_string());
}

egui::Window::new(self.name())
Expand All @@ -108,7 +69,6 @@ impl super::App for ChallengeInfoApp {

impl super::View for ChallengeInfoApp {
fn ui(&mut self, ui: &mut egui::Ui) {
self.check_info_promise();
egui::SidePanel::right("ChallengeInfoSelection")
.resizable(false)
.show_inside(ui, |ui| {
Expand Down
12 changes: 10 additions & 2 deletions src/apps/scoreboard_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,17 @@ impl ScoreBoardApp {
fn fetch(&mut self) {
self.scores = None;

let url = format!("{}api/game/scores/{}", self.url, self.challenge);
let url = format!(
"{}api/game/scores/{}",
self.url,
self.app_state
.lock()
.unwrap()
.challenges
.get_table(self.challenge)
);

log::debug!("Fetching challenge info");
log::debug!("Fetching scoreboard info");
let app_state = Arc::clone(&self.app_state);
let mut getter = Requestor::new_get(app_state, &url, true);
getter.send();
Expand Down
80 changes: 80 additions & 0 deletions src/background_processes/challenge_fetcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::helpers::{
fetchers::{RequestStatus, Requestor},
AppState, ChallengeCollection,
};
use std::sync::{Arc, Mutex};

#[derive(PartialEq, Clone, Copy)]
enum State {
Dirty,
Fetching,
Clean,
}

pub struct ChallengeFetcher {
state: State,
info_fetcher: Option<Requestor>,
app_state: Arc<Mutex<AppState>>,
}

impl Default for ChallengeFetcher {
fn default() -> Self {
Self {
info_fetcher: None,
state: State::Dirty,
app_state: Arc::new(Mutex::new(AppState::default())),
}
}
}

impl ChallengeFetcher {
pub fn new(app_state: Arc<Mutex<AppState>>) -> Self {
Self {
info_fetcher: None,
state: State::Dirty,
app_state: app_state.clone(),
}
}

pub fn tick(&mut self) {
self.fetch();
self.check_info_promise();
}

fn fetch(&mut self) {
if self.state != State::Dirty {
return;
}
log::debug!("Fetching challenge info");
self.state = State::Fetching;
let app_state = self.app_state.clone();
let my_app_state = self.app_state.clone();
self.info_fetcher = my_app_state.lock().unwrap().challenges.fetch(app_state);
}
fn check_info_promise(&mut self) {
if self.state != State::Fetching {
return;
}

let getter = &mut self.info_fetcher;

if let Some(getter) = getter {
let result = &getter.check_promise();
match result {
RequestStatus::NotStarted => {}
RequestStatus::InProgress => {}
RequestStatus::Success(data) => {
log::debug!("Challenge info fetch success: {}", data);
self.info_fetcher = None;
self.state = State::Clean;
}
RequestStatus::Failed(_) => {
self.info_fetcher = None;
self.state = State::Dirty;
}
}
self.app_state.lock().unwrap().challenges =
ChallengeCollection::from_json(&result.to_string());
}
}
}
2 changes: 2 additions & 0 deletions src/background_processes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod challenge_fetcher;
pub use challenge_fetcher::ChallengeFetcher;
66 changes: 14 additions & 52 deletions src/code_editor/editor.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::helpers::{
fetchers::{RequestStatus, Requestor},
fetchers::Requestor,
submission::{Submission, SubmissionResult},
AppState, ChallengeCollection, Challenges, Languages,
AppState, Challenges, Languages,
};
use egui::*;
use egui_commonmark::*;
use egui_notify::Toasts;
use std::sync::{Arc, Mutex};
use std::{borrow::BorrowMut, time::Duration};
use std::time::Duration;

#[derive(serde::Deserialize, serde::Serialize)]
pub struct CodeEditor {
Expand All @@ -30,14 +30,10 @@ pub struct CodeEditor {
#[serde(skip)]
selected_challenge: Challenges,

#[serde(skip)]
info_fetcher: Option<Requestor>,
#[serde(skip)]
submitter: Option<Requestor>,
#[serde(skip)]
pub app_state: Arc<Mutex<AppState>>,
#[serde(skip)]
challenges: ChallengeCollection,
}

impl Default for CodeEditor {
Expand All @@ -56,11 +52,9 @@ impl Default for CodeEditor {
last_result: SubmissionResult::NotStarted,
toasts: Toasts::default(),
submitter: None,
info_fetcher: None,
active_challenge: Challenges::None,
selected_challenge: Challenges::default(),
app_state: Arc::new(Mutex::new(AppState::default())),
challenges: ChallengeCollection::default(),
}
}
}
Expand All @@ -72,48 +66,10 @@ impl CodeEditor {
let app_state = Arc::clone(&self.app_state);
self.submitter = submission.sender(app_state, &url);
}

fn fetch(&mut self) {
if self.active_challenge == self.selected_challenge {
return;
}
log::debug!("Fetching challenge info");
self.active_challenge = self.selected_challenge;
let app_state = Arc::clone(&self.app_state);
self.info_fetcher = self.challenges.fetch(app_state);
}

fn check_info_promise(&mut self) {
let getter = &mut self.info_fetcher;

if let Some(getter) = getter {
let result = &getter.check_promise();
match result {
RequestStatus::NotStarted => {}
RequestStatus::InProgress => {}
RequestStatus::Success(_) => {
self.info_fetcher = None;
}
RequestStatus::Failed(err) => {
self.toasts
.error(format!("Error fetching challenge info: {}", err))
.set_duration(Some(Duration::from_secs(5)));

self.info_fetcher = None;
}
}
self.challenges = ChallengeCollection::from_json(&result.to_string());
self.instructions = self.challenges.get_instructions(self.selected_challenge);
}
}
}

impl CodeEditor {
pub fn panels(&mut self, ctx: &egui::Context) {
self.fetch();

self.check_info_promise();

let submission = Submission::check_sender(&mut self.submitter);
match submission {
SubmissionResult::NotStarted => {}
Expand All @@ -127,11 +83,6 @@ impl CodeEditor {
}
}

if let Some(fetcher) = self.info_fetcher.borrow_mut() {
if fetcher.refresh_context() {
ctx.request_repaint();
}
}
self.toasts.show(ctx);

egui::TopBottomPanel::bottom("code_editor_bottom").show(ctx, |_ui| {
Expand All @@ -144,6 +95,17 @@ impl CodeEditor {
}

pub fn ui(&mut self, ui: &mut egui::Ui) {
if self.active_challenge != self.selected_challenge {
self.active_challenge = self.selected_challenge;
self.instructions = self
.app_state
.lock()
.unwrap()
.challenges
.get_instructions(self.selected_challenge)
.unwrap_or("Unable to load instructions".to_string());
}

ui.vertical(|ui| {
ui.horizontal(|ui| {
let _ = ui.button("Hotkeys").on_hover_ui(nested_hotkeys_ui);
Expand Down
3 changes: 3 additions & 0 deletions src/helpers/app_state.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::helpers::ChallengeCollection;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
Expand All @@ -9,13 +10,15 @@ pub enum LoginState {
pub struct AppState {
pub counter: usize,
pub logged_in: LoginState,
pub challenges: ChallengeCollection,
}

impl Default for AppState {
fn default() -> Self {
Self {
counter: 1,
logged_in: LoginState::LoggedOut,
challenges: ChallengeCollection::default(),
}
}
}
Expand Down
15 changes: 12 additions & 3 deletions src/helpers/challenges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex};
#[derive(
Debug, Default, PartialEq, Eq, Hash, Copy, Clone, serde::Deserialize, serde::Serialize,
)]
//todo delete this and di it from the fetched data
pub enum Challenges {
C2331,
#[default]
Expand Down Expand Up @@ -91,15 +92,23 @@ impl ChallengeCollection {
}
}

pub fn get_instructions(&self, challenge: Challenges) -> String {
pub fn get_instructions(&self, challenge: Challenges) -> Option<String> {
log::debug!("Getting instructions for {}", challenge);
self.items
.iter()
.find(|c| c.command == challenge.to_string())
.map(|c| c.doc.clone())
}

pub fn get_table(&self, challenge: Challenges) -> String {
log::debug!("Getting instructions for {}", challenge);
match self
.items
.iter()
.find(|c| c.command == challenge.to_string())
{
Some(c) => c.doc.clone(),
None => String::from("No instructions found"),
Some(c) => c.table.clone(),
None => String::from("No challenge found with that command"),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod wrap_app;
pub use wrap_app::WrapApp;

pub mod apps;
pub mod background_processes;
pub mod code_editor;
pub mod components;
pub mod helpers;
Loading

0 comments on commit 198eacd

Please sign in to comment.