Add custom errors with thiserror, log thread id
This commit is contained in:
35
src-tauri/Cargo.lock
generated
35
src-tauri/Cargo.lock
generated
@@ -497,7 +497,7 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2345,7 +2345,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"png",
|
"png",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3310,7 +3310,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.16",
|
"getrandom 0.2.16",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3460,6 +3460,7 @@ dependencies = [
|
|||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-log",
|
"tauri-plugin-log",
|
||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4144,7 +4145,7 @@ dependencies = [
|
|||||||
"tauri-runtime",
|
"tauri-runtime",
|
||||||
"tauri-runtime-wry",
|
"tauri-runtime-wry",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tray-icon",
|
"tray-icon",
|
||||||
"url",
|
"url",
|
||||||
@@ -4196,7 +4197,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"syn 2.0.111",
|
"syn 2.0.111",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"time",
|
"time",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -4252,7 +4253,7 @@ dependencies = [
|
|||||||
"swift-rs",
|
"swift-rs",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4272,7 +4273,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"url",
|
"url",
|
||||||
"windows",
|
"windows",
|
||||||
"zbus",
|
"zbus",
|
||||||
@@ -4296,7 +4297,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
@@ -4360,7 +4361,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"swift-rs",
|
"swift-rs",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"toml 0.9.8",
|
"toml 0.9.8",
|
||||||
"url",
|
"url",
|
||||||
"urlpattern",
|
"urlpattern",
|
||||||
@@ -4414,11 +4415,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.17"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.17",
|
"thiserror-impl 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4434,9 +4435,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.17"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4739,7 +4740,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"png",
|
"png",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5113,7 +5114,7 @@ version = "0.38.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c"
|
checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"windows",
|
"windows",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
@@ -5623,7 +5624,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"soup3",
|
"soup3",
|
||||||
"tao-macros",
|
"tao-macros",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.18",
|
||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webkit2gtk-sys",
|
"webkit2gtk-sys",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ rand = "0.9.2"
|
|||||||
docx-rs = "0.4.18"
|
docx-rs = "0.4.18"
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
rayon = "1.11"
|
rayon = "1.11"
|
||||||
|
thiserror = "2.0.18"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.8.1", features = ["html_reports"] }
|
criterion = { version = "0.8.1", features = ["html_reports"] }
|
||||||
|
|||||||
59
src-tauri/src/errors.rs
Normal file
59
src-tauri/src/errors.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use std::io;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Serialize)]
|
||||||
|
#[serde(tag = "kind", content = "details")]
|
||||||
|
pub enum SearchError {
|
||||||
|
#[error("another thread found a solution")]
|
||||||
|
SolutionFound,
|
||||||
|
#[error("time limit exceeded")]
|
||||||
|
Timeout,
|
||||||
|
#[error("schedule is already manually filled")]
|
||||||
|
ScheduleFull,
|
||||||
|
#[error("user configuration is invalid")]
|
||||||
|
Config(String),
|
||||||
|
#[error("no solution found")]
|
||||||
|
NoSolutionFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<anyhow::Error> for SearchError {
|
||||||
|
fn from(err: anyhow::Error) -> Self {
|
||||||
|
SearchError::Config(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ExportError {
|
||||||
|
#[error("path not found: {0}")]
|
||||||
|
InvalidPath(#[from] io::Error),
|
||||||
|
#[error("docx packaging error: {0}")]
|
||||||
|
Packaging(String),
|
||||||
|
#[error("failed to open doc: {0}")]
|
||||||
|
OpenFailed(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ExportError {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
|
||||||
|
let mut s = serializer.serialize_struct("ExportError", 2)?;
|
||||||
|
|
||||||
|
s.serialize_field(
|
||||||
|
"kind",
|
||||||
|
match self {
|
||||||
|
ExportError::InvalidPath(_) => "InvalidPath",
|
||||||
|
ExportError::Packaging(_) => "Packaging",
|
||||||
|
ExportError::OpenFailed(_) => "OpenFailed",
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
s.serialize_field("details", &self.to_string())?;
|
||||||
|
|
||||||
|
s.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::{fs::File, io::Write};
|
use std::{fs::File, io::Write};
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use docx_rs::{Docx, Paragraph, Run, RunFonts, Table, TableCell, TableRow};
|
use docx_rs::{Docx, Paragraph, Run, RunFonts, Table, TableCell, TableRow};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::UserConfig,
|
config::UserConfig,
|
||||||
|
errors::ExportError,
|
||||||
schedule::MonthlySchedule,
|
schedule::MonthlySchedule,
|
||||||
slot::{month_to_greek, weekday_to_greek, Day, ShiftPosition, Slot},
|
slot::{month_to_greek, weekday_to_greek, Day, ShiftPosition, Slot},
|
||||||
workload::WorkloadTracker,
|
workload::WorkloadTracker,
|
||||||
@@ -22,7 +22,7 @@ pub trait Export {
|
|||||||
file_type: FileType,
|
file_type: FileType,
|
||||||
config: &UserConfig,
|
config: &UserConfig,
|
||||||
tracker: &WorkloadTracker,
|
tracker: &WorkloadTracker,
|
||||||
) -> anyhow::Result<()>;
|
) -> Result<(), ExportError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Export for MonthlySchedule {
|
impl Export for MonthlySchedule {
|
||||||
@@ -31,7 +31,7 @@ impl Export for MonthlySchedule {
|
|||||||
file_type: FileType,
|
file_type: FileType,
|
||||||
config: &UserConfig,
|
config: &UserConfig,
|
||||||
tracker: &WorkloadTracker,
|
tracker: &WorkloadTracker,
|
||||||
) -> anyhow::Result<()> {
|
) -> Result<(), ExportError> {
|
||||||
match file_type {
|
match file_type {
|
||||||
FileType::Txt => self.export_as_txt(config, tracker)?,
|
FileType::Txt => self.export_as_txt(config, tracker)?,
|
||||||
FileType::Docx => self.export_as_docx(config)?,
|
FileType::Docx => self.export_as_docx(config)?,
|
||||||
@@ -46,7 +46,7 @@ impl MonthlySchedule {
|
|||||||
&self,
|
&self,
|
||||||
config: &UserConfig,
|
config: &UserConfig,
|
||||||
tracker: &WorkloadTracker,
|
tracker: &WorkloadTracker,
|
||||||
) -> anyhow::Result<()> {
|
) -> Result<(), ExportError> {
|
||||||
let file = File::create("rota.txt")?;
|
let file = File::create("rota.txt")?;
|
||||||
let mut writer = std::io::BufWriter::new(file);
|
let mut writer = std::io::BufWriter::new(file);
|
||||||
|
|
||||||
@@ -157,15 +157,17 @@ impl MonthlySchedule {
|
|||||||
doc
|
doc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_as_docx(&self, config: &UserConfig) -> anyhow::Result<()> {
|
pub fn export_as_docx(&self, config: &UserConfig) -> Result<(), ExportError> {
|
||||||
let path = std::path::Path::new("rota.docx");
|
let path = std::path::Path::new("rota.docx");
|
||||||
let file = std::fs::File::create(path)?;
|
let file = std::fs::File::create(path)?;
|
||||||
let doc = self.generate_docx(config);
|
let doc = self.generate_docx(config);
|
||||||
|
|
||||||
doc.build().pack(file)?;
|
doc.build()
|
||||||
|
.pack(file)
|
||||||
|
.map_err(|e| ExportError::Packaging(e.to_string()))?;
|
||||||
|
|
||||||
tauri_plugin_opener::open_path(path, None::<&str>)
|
tauri_plugin_opener::open_path(path, None::<&str>)
|
||||||
.context("Created file but failed to open it")?;
|
.map_err(|e| ExportError::OpenFailed(e.to_string()))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -173,7 +175,6 @@ impl MonthlySchedule {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::Ok;
|
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use log::{error, info};
|
use log::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{UserConfig, UserConfigDTO},
|
config::{UserConfig, UserConfigDTO},
|
||||||
|
errors::{ExportError, SearchError},
|
||||||
export::{Export, FileType},
|
export::{Export, FileType},
|
||||||
schedule::MonthlySchedule,
|
schedule::MonthlySchedule,
|
||||||
scheduler::Scheduler,
|
scheduler::Scheduler,
|
||||||
@@ -11,6 +12,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod errors;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
pub mod resident;
|
pub mod resident;
|
||||||
pub mod schedule;
|
pub mod schedule;
|
||||||
@@ -32,16 +34,17 @@ struct AppState {
|
|||||||
fn generate(
|
fn generate(
|
||||||
config: UserConfigDTO,
|
config: UserConfigDTO,
|
||||||
state: tauri::State<'_, AppState>,
|
state: tauri::State<'_, AppState>,
|
||||||
) -> Result<MonthlySchedule, String> {
|
) -> Result<MonthlySchedule, SearchError> {
|
||||||
let mut schedule = MonthlySchedule::new();
|
let mut schedule = MonthlySchedule::new();
|
||||||
let mut tracker = WorkloadTracker::default();
|
let mut tracker = WorkloadTracker::default();
|
||||||
let config = UserConfig::try_from(config).map_err(|e| e.to_string())?;
|
let config = UserConfig::try_from(config)?;
|
||||||
let scheduler = Scheduler::new_with_config(config.clone());
|
let scheduler = Scheduler::new_with_config(config.clone());
|
||||||
|
|
||||||
scheduler
|
let solved = scheduler.run(&mut schedule, &mut tracker)?;
|
||||||
.run(&mut schedule, &mut tracker)
|
|
||||||
.inspect_err(|e| error!("{e}"))
|
if !solved {
|
||||||
.map_err(|e| e.to_string())?;
|
return Err(SearchError::NoSolutionFound);
|
||||||
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Scheduler finished successfully in {}ms",
|
"Scheduler finished successfully in {}ms",
|
||||||
@@ -60,19 +63,13 @@ fn generate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn export(state: tauri::State<'_, AppState>) -> Result<(), String> {
|
fn export(state: tauri::State<'_, AppState>) -> Result<(), ExportError> {
|
||||||
let schedule = state.schedule.lock().unwrap();
|
let schedule = state.schedule.lock().unwrap();
|
||||||
let tracker = state.tracker.lock().unwrap();
|
let tracker = state.tracker.lock().unwrap();
|
||||||
let config = state.config.lock().unwrap();
|
let config = state.config.lock().unwrap();
|
||||||
|
|
||||||
schedule
|
schedule.export(FileType::Docx, &config, &tracker)?;
|
||||||
.export(FileType::Docx, &config, &tracker)
|
schedule.export(FileType::Txt, &config, &tracker)?;
|
||||||
.inspect_err(|e| error!("{e}"))
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
schedule
|
|
||||||
.export(FileType::Txt, &config, &tracker)
|
|
||||||
.inspect_err(|e| error!("{e}"))
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let log_dir = std::env::current_dir().unwrap_or(std::path::PathBuf::from("."));
|
let log_dir = std::env::current_dir().unwrap_or(std::path::PathBuf::from("."));
|
||||||
info!("Files exported at {}", log_dir.display());
|
info!("Files exported at {}", log_dir.display());
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::UserConfig,
|
config::UserConfig,
|
||||||
|
errors::SearchError,
|
||||||
resident::ResidentId,
|
resident::ResidentId,
|
||||||
schedule::MonthlySchedule,
|
schedule::MonthlySchedule,
|
||||||
slot::Slot,
|
slot::Slot,
|
||||||
@@ -9,10 +10,12 @@ use crate::{
|
|||||||
workload::{WorkloadBounds, WorkloadTracker},
|
workload::{WorkloadBounds, WorkloadTracker},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::bail;
|
use log::info;
|
||||||
use log::warn;
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::{
|
||||||
|
current_thread_index,
|
||||||
|
iter::{IntoParallelRefIterator, ParallelIterator},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Scheduler {
|
pub struct Scheduler {
|
||||||
pub config: UserConfig,
|
pub config: UserConfig,
|
||||||
@@ -43,7 +46,7 @@ impl Scheduler {
|
|||||||
&self,
|
&self,
|
||||||
schedule: &mut MonthlySchedule,
|
schedule: &mut MonthlySchedule,
|
||||||
tracker: &mut WorkloadTracker,
|
tracker: &mut WorkloadTracker,
|
||||||
) -> anyhow::Result<bool> {
|
) -> Result<bool, SearchError> {
|
||||||
schedule.prefill(&self.config);
|
schedule.prefill(&self.config);
|
||||||
for (slot, res_id) in schedule.0.iter() {
|
for (slot, res_id) in schedule.0.iter() {
|
||||||
tracker.insert(*res_id, &self.config, *slot);
|
tracker.insert(*res_id, &self.config, *slot);
|
||||||
@@ -55,7 +58,7 @@ impl Scheduler {
|
|||||||
let slot = (0..=self.config.total_slots)
|
let slot = (0..=self.config.total_slots)
|
||||||
.find(|&slot_idx| !schedule.0.contains_key(&Slot::from(slot_idx)))
|
.find(|&slot_idx| !schedule.0.contains_key(&Slot::from(slot_idx)))
|
||||||
.map(Slot::from)
|
.map(Slot::from)
|
||||||
.ok_or_else(|| anyhow::anyhow!("Schedule is already full"))?;
|
.ok_or(SearchError::ScheduleFull)?;
|
||||||
|
|
||||||
let resident_ids = self.valid_residents(slot, schedule);
|
let resident_ids = self.valid_residents(slot, schedule);
|
||||||
let solved_in_thread = AtomicBool::new(false);
|
let solved_in_thread = AtomicBool::new(false);
|
||||||
@@ -78,7 +81,7 @@ impl Scheduler {
|
|||||||
Ok(true) => Some((local_schedule, local_tracker)),
|
Ok(true) => Some((local_schedule, local_tracker)),
|
||||||
Ok(false) => None,
|
Ok(false) => None,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Search error: {}", e);
|
info!("Thread Id: [{}] {}", current_thread_index().unwrap(), e);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,13 +105,13 @@ impl Scheduler {
|
|||||||
tracker: &mut WorkloadTracker,
|
tracker: &mut WorkloadTracker,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
solved_in_thread: &AtomicBool,
|
solved_in_thread: &AtomicBool,
|
||||||
) -> anyhow::Result<bool> {
|
) -> Result<bool, SearchError> {
|
||||||
if solved_in_thread.load(Ordering::Relaxed) {
|
if solved_in_thread.load(Ordering::Relaxed) {
|
||||||
bail!("Another thread found the solution")
|
return Err(SearchError::SolutionFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.timer.limit_exceeded() {
|
if self.timer.limit_exceeded() {
|
||||||
bail!("Time exceeded. Restrictions too tight");
|
return Err(SearchError::Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slot.is_first()
|
if !slot.is_first()
|
||||||
@@ -132,10 +135,10 @@ impl Scheduler {
|
|||||||
// sort candidates by current workload, add rng for tie breakers
|
// sort candidates by current workload, add rng for tie breakers
|
||||||
let mut valid_resident_ids = self.valid_residents(slot, schedule);
|
let mut valid_resident_ids = self.valid_residents(slot, schedule);
|
||||||
valid_resident_ids.sort_unstable_by_key(|res_id| {
|
valid_resident_ids.sort_unstable_by_key(|res_id| {
|
||||||
let type_count = tracker.get_type_count(res_id, slot.shift_type());
|
let type_count = tracker.get_type_count(res_id, slot.shift_type());
|
||||||
let workload = tracker.current_workload(res_id);
|
let workload = tracker.current_workload(res_id);
|
||||||
let tie_breaker: f64 = rand::rng().random();
|
let tie_breaker: f64 = rand::rng().random();
|
||||||
(type_count, workload, (tie_breaker * 1000.0) as usize)
|
(type_count, workload, (tie_breaker * 1000.0) as usize)
|
||||||
});
|
});
|
||||||
|
|
||||||
for id in valid_resident_ids {
|
for id in valid_resident_ids {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod integration_tests {
|
mod integration_tests {
|
||||||
use anyhow::Ok;
|
|
||||||
use rota_lib::{
|
use rota_lib::{
|
||||||
config::{ToxicPair, UserConfig},
|
config::{ToxicPair, UserConfig},
|
||||||
resident::Resident,
|
resident::Resident,
|
||||||
|
|||||||
@@ -34,16 +34,22 @@
|
|||||||
return day % 2 === 0 ? 1 : 2;
|
return day % 2 === 0 ? 1 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AppError {
|
||||||
|
kind: string;
|
||||||
|
details: string;
|
||||||
|
}
|
||||||
|
|
||||||
async function generate() {
|
async function generate() {
|
||||||
let config = rota.toDTO();
|
let config = rota.toDTO();
|
||||||
console.log(config);
|
console.log(config);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let schedule = await invoke<MonthlyScheduleDTO>("generate", { config });
|
let schedule = await invoke<MonthlyScheduleDTO>("generate", { config });
|
||||||
console.log("replyFromGenerate:", schedule);
|
console.log(schedule);
|
||||||
rota.solution = schedule;
|
rota.solution = schedule;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
const { kind, details } = error as AppError;
|
||||||
|
console.error(`[${kind}] - ${details}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +59,8 @@
|
|||||||
try {
|
try {
|
||||||
await invoke("export", { schedule });
|
await invoke("export", { schedule });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
const { kind, details } = error as AppError;
|
||||||
|
console.error(`[${kind}] - ${details}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user