use futures::{TryStream, TryStreamExt as _}; use snafu::{OptionExt as _, ResultExt as _, Snafu}; use std::num::ParseIntError; use super::{CreateListerSnafu, Day, ListError, Month, RecordingManager, Year}; pub type Hour = u8; #[derive(Debug, Snafu)] pub enum TakeError { /// hours are supposed to be directories, but this wasn't (because it didn't end with `/`) NotADirectory, /// could not parse the hour as an integer ParseIntegerError { source: ParseIntError }, } pub fn take(s: &str) -> Result<(Hour, &str), TakeError> { let (hour, rest) = s.split_once('/').context(NotADirectorySnafu)?; let hour = hour.parse().context(ParseIntegerSnafu)?; Ok((hour, rest)) } #[derive(Debug, Snafu)] pub enum HourEntryError { /// failed to get an entry from the storage operator's lister ReceiveEntryError { source: opendal::Error }, /// failed to parse the entry as a hour ParseError { source: TakeError }, } impl RecordingManager { pub async fn hours( &self, year: Year, month: Month, day: Day, ) -> Result + Unpin, ListError> { let lister = self .operator .lister(&format!("{year}/{month}/{day}/")) .await .context(CreateListerSnafu)?; Ok(lister .map_err(|error| HourEntryError::ReceiveEntryError { source: error }) .and_then(|entry| { std::future::ready( take(entry.name()) .map(|(hour, _rest)| hour) .context(ParseSnafu), ) })) } }