Skip to content

Commit 2ac699f

Browse files
authored
refactor(ext/cron): use concrete error type (denoland#26135)
1 parent 4f89225 commit 2ac699f

File tree

6 files changed

+68
-32
lines changed

6 files changed

+68
-32
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/cron/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ async-trait.workspace = true
1919
chrono = { workspace = true, features = ["now"] }
2020
deno_core.workspace = true
2121
saffron.workspace = true
22+
thiserror.workspace = true
2223
tokio.workspace = true

ext/cron/interface.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
22

3+
use crate::CronError;
34
use async_trait::async_trait;
4-
use deno_core::error::AnyError;
55

66
pub trait CronHandler {
77
type EH: CronHandle + 'static;
88

9-
fn create(&self, spec: CronSpec) -> Result<Self::EH, AnyError>;
9+
fn create(&self, spec: CronSpec) -> Result<Self::EH, CronError>;
1010
}
1111

1212
#[async_trait(?Send)]
1313
pub trait CronHandle {
14-
async fn next(&self, prev_success: bool) -> Result<bool, AnyError>;
14+
async fn next(&self, prev_success: bool) -> Result<bool, CronError>;
1515
fn close(&self);
1616
}
1717

ext/cron/lib.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@ use std::borrow::Cow;
77
use std::cell::RefCell;
88
use std::rc::Rc;
99

10+
pub use crate::interface::*;
1011
use deno_core::error::get_custom_error_class;
11-
use deno_core::error::type_error;
12-
use deno_core::error::AnyError;
1312
use deno_core::op2;
1413
use deno_core::OpState;
1514
use deno_core::Resource;
1615
use deno_core::ResourceId;
1716

18-
pub use crate::interface::*;
19-
2017
pub const UNSTABLE_FEATURE_NAME: &str = "cron";
2118

2219
deno_core::extension!(deno_cron,
@@ -49,14 +46,36 @@ impl<EH: CronHandle + 'static> Resource for CronResource<EH> {
4946
}
5047
}
5148

49+
#[derive(Debug, thiserror::Error)]
50+
pub enum CronError {
51+
#[error(transparent)]
52+
Resource(deno_core::error::AnyError),
53+
#[error("Cron name cannot exceed 64 characters: current length {0}")]
54+
NameExceeded(usize),
55+
#[error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed")]
56+
NameInvalid,
57+
#[error("Cron with this name already exists")]
58+
AlreadyExists,
59+
#[error("Too many crons")]
60+
TooManyCrons,
61+
#[error("Invalid cron schedule")]
62+
InvalidCron,
63+
#[error("Invalid backoff schedule")]
64+
InvalidBackoff,
65+
#[error(transparent)]
66+
AcquireError(#[from] tokio::sync::AcquireError),
67+
#[error(transparent)]
68+
Other(deno_core::error::AnyError),
69+
}
70+
5271
#[op2]
5372
#[smi]
5473
fn op_cron_create<C>(
5574
state: Rc<RefCell<OpState>>,
5675
#[string] name: String,
5776
#[string] cron_schedule: String,
5877
#[serde] backoff_schedule: Option<Vec<u32>>,
59-
) -> Result<ResourceId, AnyError>
78+
) -> Result<ResourceId, CronError>
6079
where
6180
C: CronHandler + 'static,
6281
{
@@ -90,7 +109,7 @@ async fn op_cron_next<C>(
90109
state: Rc<RefCell<OpState>>,
91110
#[smi] rid: ResourceId,
92111
prev_success: bool,
93-
) -> Result<bool, AnyError>
112+
) -> Result<bool, CronError>
94113
where
95114
C: CronHandler + 'static,
96115
{
@@ -102,7 +121,7 @@ where
102121
if get_custom_error_class(&err) == Some("BadResource") {
103122
return Ok(false);
104123
} else {
105-
return Err(err);
124+
return Err(CronError::Resource(err));
106125
}
107126
}
108127
};
@@ -112,17 +131,14 @@ where
112131
cron_handler.next(prev_success).await
113132
}
114133

115-
fn validate_cron_name(name: &str) -> Result<(), AnyError> {
134+
fn validate_cron_name(name: &str) -> Result<(), CronError> {
116135
if name.len() > 64 {
117-
return Err(type_error(format!(
118-
"Cron name cannot exceed 64 characters: current length {}",
119-
name.len()
120-
)));
136+
return Err(CronError::NameExceeded(name.len()));
121137
}
122138
if !name.chars().all(|c| {
123139
c.is_ascii_whitespace() || c.is_ascii_alphanumeric() || c == '_' || c == '-'
124140
}) {
125-
return Err(type_error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed"));
141+
return Err(CronError::NameInvalid);
126142
}
127143
Ok(())
128144
}

ext/cron/local.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ use std::rc::Weak;
1010
use std::sync::Arc;
1111

1212
use async_trait::async_trait;
13-
use deno_core::error::type_error;
14-
use deno_core::error::AnyError;
1513
use deno_core::futures;
1614
use deno_core::futures::FutureExt;
1715
use deno_core::unsync::spawn;
@@ -21,6 +19,7 @@ use tokio::sync::mpsc::WeakSender;
2119
use tokio::sync::OwnedSemaphorePermit;
2220
use tokio::sync::Semaphore;
2321

22+
use crate::CronError;
2423
use crate::CronHandle;
2524
use crate::CronHandler;
2625
use crate::CronSpec;
@@ -81,7 +80,7 @@ impl LocalCronHandler {
8180
async fn cron_loop(
8281
runtime_state: Rc<RefCell<RuntimeState>>,
8382
mut cron_schedule_rx: mpsc::Receiver<(String, bool)>,
84-
) -> Result<(), AnyError> {
83+
) -> Result<(), CronError> {
8584
loop {
8685
let earliest_deadline = runtime_state
8786
.borrow()
@@ -154,7 +153,7 @@ impl LocalCronHandler {
154153
impl RuntimeState {
155154
fn get_ready_crons(
156155
&mut self,
157-
) -> Result<Vec<(String, WeakSender<()>)>, AnyError> {
156+
) -> Result<Vec<(String, WeakSender<()>)>, CronError> {
158157
let now = chrono::Utc::now().timestamp_millis() as u64;
159158

160159
let ready = {
@@ -191,7 +190,7 @@ impl RuntimeState {
191190
impl CronHandler for LocalCronHandler {
192191
type EH = CronExecutionHandle;
193192

194-
fn create(&self, spec: CronSpec) -> Result<Self::EH, AnyError> {
193+
fn create(&self, spec: CronSpec) -> Result<Self::EH, CronError> {
195194
// Ensure that the cron loop is started.
196195
self.cron_loop_join_handle.get_or_init(|| {
197196
let (cron_schedule_tx, cron_schedule_rx) =
@@ -208,17 +207,17 @@ impl CronHandler for LocalCronHandler {
208207
let mut runtime_state = self.runtime_state.borrow_mut();
209208

210209
if runtime_state.crons.len() > MAX_CRONS {
211-
return Err(type_error("Too many crons"));
210+
return Err(CronError::TooManyCrons);
212211
}
213212
if runtime_state.crons.contains_key(&spec.name) {
214-
return Err(type_error("Cron with this name already exists"));
213+
return Err(CronError::AlreadyExists);
215214
}
216215

217216
// Validate schedule expression.
218217
spec
219218
.cron_schedule
220219
.parse::<saffron::Cron>()
221-
.map_err(|_| type_error("Invalid cron schedule"))?;
220+
.map_err(|_| CronError::InvalidCron)?;
222221

223222
// Validate backoff_schedule.
224223
if let Some(backoff_schedule) = &spec.backoff_schedule {
@@ -263,7 +262,7 @@ struct Inner {
263262

264263
#[async_trait(?Send)]
265264
impl CronHandle for CronExecutionHandle {
266-
async fn next(&self, prev_success: bool) -> Result<bool, AnyError> {
265+
async fn next(&self, prev_success: bool) -> Result<bool, CronError> {
267266
self.inner.borrow_mut().permit.take();
268267

269268
if self
@@ -300,7 +299,7 @@ impl CronHandle for CronExecutionHandle {
300299
}
301300
}
302301

303-
fn compute_next_deadline(cron_expression: &str) -> Result<u64, AnyError> {
302+
fn compute_next_deadline(cron_expression: &str) -> Result<u64, CronError> {
304303
let now = chrono::Utc::now();
305304

306305
if let Ok(test_schedule) = env::var("DENO_CRON_TEST_SCHEDULE_OFFSET") {
@@ -311,19 +310,21 @@ fn compute_next_deadline(cron_expression: &str) -> Result<u64, AnyError> {
311310

312311
let cron = cron_expression
313312
.parse::<saffron::Cron>()
314-
.map_err(|_| anyhow::anyhow!("invalid cron expression"))?;
313+
.map_err(|_| CronError::InvalidCron)?;
315314
let Some(next_deadline) = cron.next_after(now) else {
316-
return Err(anyhow::anyhow!("invalid cron expression"));
315+
return Err(CronError::InvalidCron);
317316
};
318317
Ok(next_deadline.timestamp_millis() as u64)
319318
}
320319

321-
fn validate_backoff_schedule(backoff_schedule: &[u32]) -> Result<(), AnyError> {
320+
fn validate_backoff_schedule(
321+
backoff_schedule: &[u32],
322+
) -> Result<(), CronError> {
322323
if backoff_schedule.len() > MAX_BACKOFF_COUNT {
323-
return Err(type_error("Invalid backoff schedule"));
324+
return Err(CronError::InvalidBackoff);
324325
}
325326
if backoff_schedule.iter().any(|s| *s > MAX_BACKOFF_MS) {
326-
return Err(type_error("Invalid backoff schedule"));
327+
return Err(CronError::InvalidBackoff);
327328
}
328329
Ok(())
329330
}

runtime/errors.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use deno_core::error::AnyError;
1616
use deno_core::serde_json;
1717
use deno_core::url;
1818
use deno_core::ModuleResolutionError;
19+
use deno_cron::CronError;
1920
use std::env;
2021
use std::error::Error;
2122
use std::io;
@@ -156,6 +157,22 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
156157
}
157158
}
158159

160+
pub fn get_cron_error_class(e: &CronError) -> &'static str {
161+
match e {
162+
CronError::Resource(e) => {
163+
deno_core::error::get_custom_error_class(e).unwrap_or("Error")
164+
}
165+
CronError::NameExceeded(_) => "TypeError",
166+
CronError::NameInvalid => "TypeError",
167+
CronError::AlreadyExists => "TypeError",
168+
CronError::TooManyCrons => "TypeError",
169+
CronError::InvalidCron => "TypeError",
170+
CronError::InvalidBackoff => "TypeError",
171+
CronError::AcquireError(_) => "Error",
172+
CronError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
173+
}
174+
}
175+
159176
fn get_canvas_error(e: &CanvasError) -> &'static str {
160177
match e {
161178
CanvasError::UnsupportedColorType(_) => "TypeError",
@@ -194,7 +211,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
194211
.or_else(|| deno_web::get_error_class_name(e))
195212
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
196213
.or_else(|| deno_websocket::get_network_error_class_name(e))
197-
.or_else(|| deno_websocket::get_network_error_class_name(e))
214+
.or_else(|| e.downcast_ref::<CronError>().map(get_cron_error_class))
198215
.or_else(|| e.downcast_ref::<CanvasError>().map(get_canvas_error))
199216
.or_else(|| e.downcast_ref::<CacheError>().map(get_cache_error))
200217
.or_else(|| {

0 commit comments

Comments
 (0)