Add type ChannelName

This commit is contained in:
Paul Campbell 2024-01-23 07:21:19 +00:00
parent 9a7287c1d9
commit c2f4070a36
6 changed files with 62 additions and 16 deletions

View file

@ -2,8 +2,10 @@ use crate::prelude::*;
use crate::network::{NetUrl, NetworkEnv}; use crate::network::{NetUrl, NetworkEnv};
pub fn find(site: &str, channel_name: &str, e: &NetworkEnv) -> Result<NetUrl> { use super::ChannelName;
if let Some(channel_prefix) = channel_name.chars().next() {
pub fn find(site: &str, channel_name: &ChannelName, e: &NetworkEnv) -> Result<NetUrl> {
if let Some(channel_prefix) = channel_name.0.chars().next() {
if channel_prefix != '@' { if channel_prefix != '@' {
return Err(anyhow!( return Err(anyhow!(
"Channel Name must begin with an '@': {}", "Channel Name must begin with an '@': {}",
@ -51,7 +53,7 @@ mod tests {
download_as_mp3: stub_network_download_as_mp3(), download_as_mp3: stub_network_download_as_mp3(),
}; };
//when //when
let result = find("site", "@channel", &network_env)?; let result = find("site", &ChannelName::from("@channel"), &network_env)?;
//then //then
assert_eq!(result, NetUrl::from("the-rss-url")); assert_eq!(result, NetUrl::from("the-rss-url"));
Ok(()) Ok(())
@ -66,7 +68,11 @@ mod tests {
download_as_mp3: stub_network_download_as_mp3(), download_as_mp3: stub_network_download_as_mp3(),
}; };
//when //when
let result = find("site", "invalid-channel-name", &network_env); let result = find(
"site",
&ChannelName::from("invalid-channel-name"),
&network_env,
);
//then //then
assert!(result.is_err()); assert!(result.is_err());
Ok(()) Ok(())

View file

@ -12,3 +12,16 @@ pub fn get(url: &NetUrl, e: &NetworkEnv) -> Result<Feed> {
let channel = Feed::read_from(&content[..]).context("Could not parse RSS feed")?; let channel = Feed::read_from(&content[..]).context("Could not parse RSS feed")?;
Ok(channel) Ok(channel)
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ChannelName(pub String);
impl ChannelName {
pub fn from(channel_name: &str) -> Self {
Self(channel_name.to_string())
}
}
impl std::fmt::Display for ChannelName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.as_str())
}
}

View file

@ -1,15 +1,15 @@
use crate::prelude::*; use crate::{feed::ChannelName, prelude::*};
use crate::file::FileEnv; use crate::file::FileEnv;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
pub fn lines_from(file_name: &str, e: &FileEnv) -> Result<Vec<String>> { pub fn lines_from(file_name: &str, e: &FileEnv) -> Result<Vec<ChannelName>> {
let file = (e.open)(file_name).context(format!("Opening file: {file_name}"))?; let file = (e.open)(file_name).context(format!("Opening file: {file_name}"))?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
let mut lines = vec![]; let mut lines = vec![];
for line in reader.lines().flatten() { for line in reader.lines().flatten() {
if line.starts_with('@') { if line.starts_with('@') {
lines.push(line); lines.push(ChannelName(line));
} }
} }
Ok(lines) Ok(lines)
@ -41,7 +41,13 @@ mod tests {
//then //then
drop(dir); drop(dir);
assert_eq!(result, ["@sub1", "@sub2", "@sub3"]); assert_eq!(
result,
["@sub1", "@sub2", "@sub3"]
.into_iter()
.map(ChannelName::from)
.collect::<Vec<ChannelName>>()
);
Ok(()) Ok(())
} }
@ -64,7 +70,13 @@ mod tests {
//then //then
drop(dir); drop(dir);
assert_eq!(result, ["@sub1", "@sub2", "@sub3"]); assert_eq!(
result,
["@sub1", "@sub2", "@sub3"]
.into_iter()
.map(ChannelName::from)
.collect::<Vec<ChannelName>>()
);
Ok(()) Ok(())
} }
@ -87,7 +99,13 @@ mod tests {
//then //then
drop(dir); drop(dir);
assert_eq!(result, ["@sub1", "@sub3"]); assert_eq!(
result,
["@sub1", "@sub3"]
.into_iter()
.map(ChannelName::from)
.collect::<Vec<ChannelName>>()
);
Ok(()) Ok(())
} }
} }

View file

@ -34,7 +34,7 @@ pub fn run(site: &str, a: &Args, e: Env) -> Result<()> {
if let Some(link) = entry.links().first() { if let Some(link) = entry.links().first() {
if !history::find(link, &a.history, &e.file).context("Finding history")? { if !history::find(link, &a.history, &e.file).context("Finding history")? {
println!("Downloading {}: {}", &channel_name, entry.title().as_str()); println!("Downloading {}: {}", &channel_name, entry.title().as_str());
(e.network.download_as_mp3)(&NetUrl(link.href.clone())) (e.network.download_as_mp3)(&NetUrl(link.href.clone()), &channel_name)
.context("Downloading as MP3")?; .context("Downloading as MP3")?;
history::add(link, &a.history, &e.file).context("Adding to history")?; history::add(link, &a.history, &e.file).context("Adding to history")?;
} }

View file

@ -1,6 +1,6 @@
use std::process::Command; use std::process::Command;
use crate::prelude::*; use crate::{feed::ChannelName, prelude::*};
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NetUrl(pub String); pub struct NetUrl(pub String);
@ -19,7 +19,7 @@ pub type NetworkFetchAsTextFn = Box<dyn Fn(&NetUrl) -> Result<String>>;
pub type NetworkFetchAsBytesFn = Box<dyn Fn(&NetUrl) -> Result<bytes::Bytes>>; pub type NetworkFetchAsBytesFn = Box<dyn Fn(&NetUrl) -> Result<bytes::Bytes>>;
pub type NetworkDownloadAsMp3Fn = Box<dyn Fn(&NetUrl) -> Result<()>>; pub type NetworkDownloadAsMp3Fn = Box<dyn Fn(&NetUrl, &ChannelName) -> Result<()>>;
pub struct NetworkEnv { pub struct NetworkEnv {
pub fetch_as_text: NetworkFetchAsTextFn, pub fetch_as_text: NetworkFetchAsTextFn,
@ -41,12 +41,14 @@ impl Default for NetworkEnv {
.bytes() .bytes()
.context(format!("Parsing bytes from body of response for {}", url)) .context(format!("Parsing bytes from body of response for {}", url))
}), }),
download_as_mp3: Box::new(|url| { download_as_mp3: Box::new(|url, channel_name| {
let cmd = "yt-dlp"; let cmd = "yt-dlp";
let output = Command::new(cmd) let output = Command::new(cmd)
.arg("--extract-audio") .arg("--extract-audio")
.arg("--audio-format") .arg("--audio-format")
.arg("mp3") .arg("mp3")
.arg("-p")
.arg(format!("~/Music/{}", channel_name))
.arg(&url.0) .arg(&url.0)
.output() .output()
.with_context(|| { .with_context(|| {

View file

@ -11,6 +11,7 @@ use anyhow::Context;
use tempfile::{tempdir, TempDir}; use tempfile::{tempdir, TempDir};
use crate::{ use crate::{
feed::ChannelName,
file::{FileAppendLineFn, FileOpenFn}, file::{FileAppendLineFn, FileOpenFn},
network::{NetUrl, NetworkDownloadAsMp3Fn, NetworkFetchAsBytesFn, NetworkFetchAsTextFn}, network::{NetUrl, NetworkDownloadAsMp3Fn, NetworkFetchAsBytesFn, NetworkFetchAsTextFn},
prelude::*, prelude::*,
@ -70,7 +71,7 @@ pub fn mock_file_open(real_paths: HashMap<String, String>) -> FileOpenFn {
} }
pub fn mock_network_download_as_mp3(tx: Sender<NetUrl>) -> NetworkDownloadAsMp3Fn { pub fn mock_network_download_as_mp3(tx: Sender<NetUrl>) -> NetworkDownloadAsMp3Fn {
Box::new(move |url: &NetUrl| { Box::new(move |url: &NetUrl, _channel_name: &ChannelName| {
tx.send(url.clone())?; tx.send(url.clone())?;
Ok(()) Ok(())
}) })
@ -84,5 +85,11 @@ pub fn stub_network_fetch_as_bytes() -> NetworkFetchAsBytesFn {
Box::new(|url: &NetUrl| Err(anyhow!("Not implemented: network_fetch_as_bytes: {}", url))) Box::new(|url: &NetUrl| Err(anyhow!("Not implemented: network_fetch_as_bytes: {}", url)))
} }
pub fn stub_network_download_as_mp3() -> NetworkDownloadAsMp3Fn { pub fn stub_network_download_as_mp3() -> NetworkDownloadAsMp3Fn {
Box::new(|url: &NetUrl| Err(anyhow!("Not implemented: network_download_as_mp3: {}", url))) Box::new(|url: &NetUrl, channel_name: &ChannelName| {
Err(anyhow!(
"Not implemented: network_download_as_mp3: ({}) {}",
channel_name,
url,
))
})
} }