fix(calendar): the source constructor reports CalendarError
This commit is contained in:
1 parent
5eef154fcb
commit
15d3478429
3 files changed
+23
-10
No files matched your search
@@ -31,6 +31,13 @@ pub enum CalendarError {
|
|||||||
/// The request never got an answer (DNS, connect, timeout).
|
/// The request never got an answer (DNS, connect, timeout).
|
||||||
#[error("network error: {0}")]
|
#[error("network error: {0}")]
|
||||||
Network(String),
|
Network(String),
|
||||||
|
/// A calendar's configuration does not resolve into a client: a
|
||||||
|
/// kind-specific piece is missing (a `url`, a `calendar_id`, a
|
||||||
|
/// credential). Config validation runs at load, so this surfaces
|
||||||
|
/// where the feed is built, and the daemon refuses to start rather
|
||||||
|
/// than poll an under-specified feed.
|
||||||
|
#[error("calendar config: {0}")]
|
||||||
|
Config(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The longest body snippet an [`CalendarError::Api`] keeps: enough for
|
/// The longest body snippet an [`CalendarError::Api`] keeps: enough for
|
||||||
|
|||||||
@@ -64,7 +64,6 @@
|
|||||||
//! time, `calendar:<name>` keys, one `Source` over the configured feeds
|
//! time, `calendar:<name>` keys, one `Source` over the configured feeds
|
||||||
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(missing_docs)]
|
|
||||||
|
|
||||||
pub mod caldav;
|
pub mod caldav;
|
||||||
pub mod calendar;
|
pub mod calendar;
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ use serde_json::{Value, json};
|
|||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use time::format_description::well_known::Rfc3339;
|
use time::format_description::well_known::Rfc3339;
|
||||||
|
|
||||||
use crate::{CaldavCalendar, Calendar, CalendarEvent, GoogleCalendar, IcsCalendar, Window};
|
use crate::{
|
||||||
|
CaldavCalendar, Calendar, CalendarError, CalendarEvent, GoogleCalendar, IcsCalendar, Window,
|
||||||
|
};
|
||||||
|
|
||||||
/// The source name the calendar reminders ingest under; registered on
|
/// The source name the calendar reminders ingest under; registered on
|
||||||
/// the consumer's dispatcher, so allowlists and ingest doors can name
|
/// the consumer's dispatcher, so allowlists and ingest doors can name
|
||||||
@@ -85,7 +87,7 @@ pub(crate) struct Feed {
|
|||||||
/// before construction: the api key wins for Google, else the token
|
/// before construction: the api key wins for Google, else the token
|
||||||
/// (config first, then [`TOKEN_ENV`]), and a feed takes its token or
|
/// (config first, then [`TOKEN_ENV`]), and a feed takes its token or
|
||||||
/// polls anonymous, which is how most published feed URLs work.
|
/// polls anonymous, which is how most published feed URLs work.
|
||||||
fn client_for(calendar: &CalendarWatch) -> Result<Arc<dyn Calendar>, String> {
|
fn client_for(calendar: &CalendarWatch) -> Result<Arc<dyn Calendar>, CalendarError> {
|
||||||
match calendar.kind {
|
match calendar.kind {
|
||||||
CalendarKind::Google => {
|
CalendarKind::Google => {
|
||||||
let id = calendar
|
let id = calendar
|
||||||
@@ -93,13 +95,13 @@ fn client_for(calendar: &CalendarWatch) -> Result<Arc<dyn Calendar>, String> {
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|id| !id.is_empty())
|
.filter(|id| !id.is_empty())
|
||||||
.ok_or_else(|| "google needs calendar_id".to_owned())?;
|
.ok_or_else(|| CalendarError::Config("google needs calendar_id".to_owned()))?;
|
||||||
if let Some(key) = calendar.resolved_api_key() {
|
if let Some(key) = calendar.resolved_api_key() {
|
||||||
return Ok(Arc::new(GoogleCalendar::with_api_key(key, id)));
|
return Ok(Arc::new(GoogleCalendar::with_api_key(key, id)));
|
||||||
}
|
}
|
||||||
let token = calendar
|
let token = calendar
|
||||||
.resolved_token()
|
.resolved_token()
|
||||||
.ok_or_else(|| "google needs api_key or token".to_owned())?;
|
.ok_or_else(|| CalendarError::Config("google needs api_key or token".to_owned()))?;
|
||||||
Ok(Arc::new(GoogleCalendar::new(token, id)))
|
Ok(Arc::new(GoogleCalendar::new(token, id)))
|
||||||
}
|
}
|
||||||
CalendarKind::Ics => {
|
CalendarKind::Ics => {
|
||||||
@@ -108,7 +110,7 @@ fn client_for(calendar: &CalendarWatch) -> Result<Arc<dyn Calendar>, String> {
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|url| !url.is_empty())
|
.filter(|url| !url.is_empty())
|
||||||
.ok_or_else(|| "ics needs url".to_owned())?;
|
.ok_or_else(|| CalendarError::Config("ics needs url".to_owned()))?;
|
||||||
let mut feed = IcsCalendar::new(url);
|
let mut feed = IcsCalendar::new(url);
|
||||||
if let Some(token) = calendar.resolved_token() {
|
if let Some(token) = calendar.resolved_token() {
|
||||||
feed = feed.with_token(token);
|
feed = feed.with_token(token);
|
||||||
@@ -121,7 +123,7 @@ fn client_for(calendar: &CalendarWatch) -> Result<Arc<dyn Calendar>, String> {
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|url| !url.is_empty())
|
.filter(|url| !url.is_empty())
|
||||||
.ok_or_else(|| "caldav needs url".to_owned())?;
|
.ok_or_else(|| CalendarError::Config("caldav needs url".to_owned()))?;
|
||||||
let mut dav = CaldavCalendar::new(url);
|
let mut dav = CaldavCalendar::new(url);
|
||||||
// A username means Basic auth, the self-hosted norm, and
|
// A username means Basic auth, the self-hosted norm, and
|
||||||
// the password must have resolved at validation; a token
|
// the password must have resolved at validation; a token
|
||||||
@@ -134,7 +136,9 @@ fn client_for(calendar: &CalendarWatch) -> Result<Arc<dyn Calendar>, String> {
|
|||||||
.filter(|user| !user.is_empty())
|
.filter(|user| !user.is_empty())
|
||||||
{
|
{
|
||||||
let password = calendar.resolved_password().ok_or_else(|| {
|
let password = calendar.resolved_password().ok_or_else(|| {
|
||||||
"caldav needs a password: set password or CALDAV_PASSWORD".to_owned()
|
CalendarError::Config(
|
||||||
|
"caldav needs a password: set password or CALDAV_PASSWORD".to_owned(),
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
dav = dav.with_basic(user, password);
|
dav = dav.with_basic(user, password);
|
||||||
} else if let Some(token) = calendar.resolved_token() {
|
} else if let Some(token) = calendar.resolved_token() {
|
||||||
@@ -257,7 +261,7 @@ impl CalendarSource {
|
|||||||
dispatcher: &Arc<Dispatcher>,
|
dispatcher: &Arc<Dispatcher>,
|
||||||
clock: Arc<dyn Clock>,
|
clock: Arc<dyn Clock>,
|
||||||
calendars: &[CalendarWatch],
|
calendars: &[CalendarWatch],
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, CalendarError> {
|
||||||
let mut feeds = Vec::with_capacity(calendars.len());
|
let mut feeds = Vec::with_capacity(calendars.len());
|
||||||
for calendar in calendars {
|
for calendar in calendars {
|
||||||
feeds.push(Feed {
|
feeds.push(Feed {
|
||||||
@@ -330,7 +334,10 @@ fn store_feeds(store: &crate::store::CalendarStore) -> Result<Vec<Feed>, String>
|
|||||||
for calendar in &calendars {
|
for calendar in &calendars {
|
||||||
feeds.push(Feed {
|
feeds.push(Feed {
|
||||||
name: calendar.name.trim().to_owned(),
|
name: calendar.name.trim().to_owned(),
|
||||||
client: client_for(calendar)?,
|
// The Source seam reports through `SourceError`, whose text
|
||||||
|
// carries the refusal; the typed config error stringifies
|
||||||
|
// at that boundary.
|
||||||
|
client: client_for(calendar).map_err(|err| err.to_string())?,
|
||||||
leads: calendar.leads(),
|
leads: calendar.leads(),
|
||||||
horizon_hours: calendar.horizon(),
|
horizon_hours: calendar.horizon(),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user