reorganise modules

This commit is contained in:
Aaron Manning
2025-09-01 10:07:28 +10:00
parent 2b55c5b0af
commit 5291730793
5 changed files with 181 additions and 170 deletions

142
src/manage.rs Normal file
View File

@@ -0,0 +1,142 @@
use std::{
fs,
path,
borrow::Cow,
collections::{
BTreeMap,
HashMap,
},
};
use anyhow::Context;
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct Specification<'a> {
files: HashMap<Cow<'a, str>, Cow<'a, path::Path>>,
/// This is a collection of episodes, where each entry contains a `Vec` of
/// episodes to allow for the possibility that multiple episodes have the
/// same timestamp.
feed: BTreeMap<chrono::NaiveDateTime, Vec<Episode<'a>>>,
pub(in crate) image_url: Option<Cow<'a, str>>,
}
impl<'a> Specification<'a> {
/// Reads from the specification file from a given path, or gives a default
/// if the file doesn't exist.
pub(crate) fn read_from_with_default(
path: &path::Path
) -> Result<Self, anyhow::Error> {
Ok(if path.is_file() {
toml::from_str(&fs::read_to_string(&path)?[..])?
} else {
Specification::default()
})
}
/// Reads from the specification file from a given path.
pub(crate) fn read_from(
path: &path::Path
) -> Result<Self, anyhow::Error> {
Ok(if path.is_file() {
toml::from_str(&fs::read_to_string(&path)?[..])?
} else {
anyhow::bail!("could not find specification for the desired podcast")
})
}
/// Writes the specification to the specific file path.
pub(crate) fn write_to(&self, path: &path::Path) -> Result<(), anyhow::Error> {
Ok(fs::write(path, toml::to_string(self)?.as_bytes())?)
}
pub(crate) fn feed_iter(&self) -> impl Iterator<Item = (&chrono::NaiveDateTime, &Vec<Episode<'a>>)> {
self.feed.iter()
}
pub(crate) fn feed_iter_mut(&mut self) -> impl Iterator<Item = (&chrono::NaiveDateTime, &mut Vec<Episode<'a>>)> {
self.feed.iter_mut()
}
pub(crate) fn path_from_id(&self, id: &str) -> Option<&path::Path> {
self.files.get(id).map(|v| &**v)
}
pub(crate) fn into_feed_and_files(
self
) -> (
BTreeMap<chrono::NaiveDateTime, Vec<Episode<'a>>>,
HashMap<Cow<'a, str>, Cow<'a, path::Path>>,
) {
(self.feed, self.files)
}
pub(crate) fn insert_into_files(
&mut self,
id: impl Into<Cow<'a, str>>,
path: impl Into<Cow<'a, path::Path>>,
) -> anyhow::Result<()> {
self.files.insert(
id.into(),
path.into()
)
.context("insertion of episode with duplicate id")
.map(|_| ())
}
pub(crate) fn insert_into_feed(
&mut self,
published: chrono::NaiveDateTime,
episode: Episode<'a>
) {
match self.feed.get_mut(&published) {
Some(existing) => {
existing.push(episode)
},
None => {
self.feed.insert(
published,
vec![episode],
);
}
}
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub (crate) struct Episode<'a> {
/// Episode title.
title: Cow<'a, str>,
/// Show notes pulled from description or summary tag.
show_notes: Option<Cow<'a, str>>,
/// This is the GUID or the URL if the GUID is not present.
id: Cow<'a, str>,
/// If the episode exists in the latest version of the feed.
pub(crate) current: bool,
/// Flag to keep track of which episodes have been listened to.
#[serde(default)]
pub(crate) listened: bool,
}
impl<'a> Episode<'a> {
pub(crate) fn new_downloaded(
title: impl Into<Cow<'a, str>>,
show_notes: Option<Cow<'a, str>>,
id: impl Into<Cow<'a, str>>,
) -> Self {
Self {
title: title.into(),
show_notes,
id: id.into(),
current: true,
listened: false,
}
}
pub(crate) fn title(&self) -> &str {
self.title.as_ref()
}
pub(crate) fn id(&self) -> &str {
&self.id
}
}