Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
51 lines
1.7 KiB
Rust
51 lines
1.7 KiB
Rust
//! Typed errors for kei-scheduler. `Error` is the public wrapper;
|
|
//! `ParseError` surfaces trigger-spec parse failures separately so
|
|
//! callers (and tests) can discriminate without string-matching.
|
|
|
|
use thiserror::Error;
|
|
|
|
/// Trigger-spec parse failures. Pure function — no DB contact.
|
|
#[derive(Debug, Error, PartialEq, Eq)]
|
|
pub enum ParseError {
|
|
#[error("unknown trigger_kind '{0}' — expected cron / at / interval")]
|
|
UnknownKind(String),
|
|
#[error("invalid cron expression '{0}': {1}")]
|
|
InvalidCron(String, String),
|
|
#[error("invalid ISO-8601 datetime '{0}' — expected YYYY-MM-DDTHH:MM:SSZ")]
|
|
InvalidIsoDatetime(String),
|
|
#[error("invalid interval '{0}' — expected positive integer seconds")]
|
|
InvalidInterval(String),
|
|
}
|
|
|
|
/// Public scheduler error. Wraps rusqlite + anyhow + ParseError.
|
|
#[derive(Debug, Error)]
|
|
pub enum Error {
|
|
#[error(transparent)]
|
|
Parse(#[from] ParseError),
|
|
#[error("sqlite: {0}")]
|
|
Sqlite(#[from] rusqlite::Error),
|
|
#[error("task not found: id={0}")]
|
|
NotFound(i64),
|
|
#[error("task name already exists: '{0}'")]
|
|
NameExists(String),
|
|
#[error("io: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
#[error(transparent)]
|
|
Other(#[from] anyhow::Error),
|
|
}
|
|
|
|
impl Error {
|
|
/// Inspect a rusqlite error and reclassify `UNIQUE constraint
|
|
/// failed: scheduler_tasks.name` into a typed `NameExists`. Other
|
|
/// SQLite errors pass through unchanged.
|
|
pub fn from_insert(err: rusqlite::Error, name: &str) -> Self {
|
|
let msg = err.to_string();
|
|
if msg.contains("UNIQUE constraint failed")
|
|
&& msg.contains("scheduler_tasks.name")
|
|
{
|
|
Self::NameExists(name.to_string())
|
|
} else {
|
|
Self::Sqlite(err)
|
|
}
|
|
}
|
|
}
|