i15-anyhow-context #17

Merged
kemitix merged 2 commits from i15-anyhow-context into main 2023-08-13 16:11:16 +01:00
10 changed files with 57 additions and 31 deletions
Showing only changes of commit 29679ba9d1 - Show all commits

View file

@ -12,15 +12,15 @@ pub fn find(site: &str, channel_name: &str, e: &NetworkEnv) -> Result<String> {
} }
} }
let channel_url = format!("{}{}", site, channel_name); let channel_url = format!("{}{}", site, channel_name);
let response = let response = (e.fetch_as_text)(&channel_url)
(e.fetch_as_text)(&channel_url).with_context(|| format!("Fetching {}", channel_url))?; .context(format!("Fetching channel to find RSS: {}", channel_url))?;
let rss_url = scraper::Html::parse_document(&response) let rss_url = scraper::Html::parse_document(&response)
.select(&scraper::Selector::parse("link[title='RSS']").unwrap()) .select(&scraper::Selector::parse("link[title='RSS']").unwrap())
.next() .next()
.unwrap() .context("No RSS link found")?
.value() .value()
.attr("href") .attr("href")
.unwrap() .context("No href attribute found in RSS link")?
.to_string(); .to_string();
Ok(rss_url) Ok(rss_url)
} }

View file

@ -8,7 +8,7 @@ mod find;
pub use find::find; pub use find::find;
pub fn get(url: &str, e: &NetworkEnv) -> Result<Feed> { pub fn get(url: &str, e: &NetworkEnv) -> Result<Feed> {
let content = (e.fetch_as_bytes)(url)?; let content = (e.fetch_as_bytes)(url).context(format!("Fetching feed: {}", url))?;
let channel = Feed::read_from(&content[..])?; let channel = Feed::read_from(&content[..]).context("Could not parse RSS feed")?;
Ok(channel) Ok(channel)
} }

View file

@ -21,9 +21,7 @@ impl FileEnv {
Self { Self {
open: Box::new(move |file_name| { open: Box::new(move |file_name| {
let path = format!("{}/{}", &open_dir, file_name); let path = format!("{}/{}", &open_dir, file_name);
let file = File::open(&path).with_context(|| { let file = File::open(&path).context(format!("Opening file: {path}"))?;
format!("FileEnv::open: file_name={file_name}, path={path}")
})?;
Ok(file) Ok(file)
}), }),
append_line: Box::new(move |file_name, line| { append_line: Box::new(move |file_name, line| {
@ -32,8 +30,8 @@ impl FileEnv {
.write(true) .write(true)
.append(true) .append(true)
.create(true) .create(true)
.open(path) .open(&path)
.unwrap(); .context(format!("Appending to file: {path}"))?;
writeln!(file, "{}", line)?; writeln!(file, "{}", line)?;
Ok(()) Ok(())
}), }),

View file

@ -4,7 +4,7 @@ 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<String>> {
let file = (e.open)(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() {

View file

@ -4,7 +4,7 @@ use crate::prelude::*;
use super::Link; use super::Link;
pub fn add(link: &Link, file_name: &str, e: &FileEnv) -> Result<()> { pub fn add(link: &Link, file_name: &str, e: &FileEnv) -> Result<()> {
(e.append_line)(file_name, &link.href)?; (e.append_line)(file_name, &link.href).context(format!("Appending to file: {}", file_name))?;
Ok(()) Ok(())
} }

View file

@ -5,10 +5,10 @@ use std::io::{BufRead, BufReader};
use super::Link; use super::Link;
pub fn find(link: &Link, file_name: &str, e: &FileEnv) -> Result<bool> { pub fn find(link: &Link, file_name: &str, e: &FileEnv) -> Result<bool> {
let file = (e.open)(file_name)?; let file = (e.open)(file_name).context(format!("Opening file: {file_name}"))?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
for line in reader.lines() { for line in reader.lines() {
let line = line?; let line = line.context(format!("Reading line in file: {file_name}"))?;
if line == link.href { if line == link.href {
return Ok(true); // is already downloaded return Ok(true); // is already downloaded
} }

View file

@ -20,15 +20,20 @@ pub struct Env {
} }
pub fn run(site: &str, a: &Args, e: Env) -> Result<()> { pub fn run(site: &str, a: &Args, e: Env) -> Result<()> {
for channel_name in file::read::lines_from(&a.subscriptions, &e.file)? { for channel_name in
file::read::lines_from(&a.subscriptions, &e.file).context("Reading subscriptions")?
{
println!("Channel: {}", channel_name); println!("Channel: {}", channel_name);
let feed_url = feed::find(site, &channel_name, &e.network)?; let feed_url = feed::find(site, &channel_name, &e.network).context("Finding channel")?;
for entry in feed::get(&feed_url, &e.network)?.entries() { for entry in feed::get(&feed_url, &e.network)
.context("Fetching channel")?
.entries()
{
if let Some(link) = entry.links().get(0).cloned() { if let Some(link) = entry.links().get(0).cloned() {
if !history::find(&link, &a.history, &e.file)? { 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)(&link.href)?; (e.network.download_as_mp3)(&link.href).context("Downloading as MP3")?;
history::add(&link, &a.history, &e.file)?; history::add(&link, &a.history, &e.file).context("Adding to history")?;
} }
} }
} }

View file

@ -17,7 +17,8 @@ fn main() -> Result<()> {
network: NetworkEnv::default(), network: NetworkEnv::default(),
file: FileEnv::create(&args), file: FileEnv::create(&args),
}, },
)?; )
.context("Running")?;
println!("Done"); println!("Done");
Ok(()) Ok(())

View file

@ -16,20 +16,41 @@ pub struct NetworkEnv {
impl Default for NetworkEnv { impl Default for NetworkEnv {
fn default() -> Self { fn default() -> Self {
Self { Self {
fetch_as_text: Box::new(|url| Ok(reqwest::blocking::get(url)?.text()?)), fetch_as_text: Box::new(|url| {
fetch_as_bytes: Box::new(|url| Ok(reqwest::blocking::get(url)?.bytes()?)), reqwest::blocking::get(url)
.context(format!("Fetching {}", url))?
.text()
.context(format!("Parsing text from body of response for {}", url))
}),
fetch_as_bytes: Box::new(|url| {
reqwest::blocking::get(url)
.context(format!("Fetching {}", url))?
.bytes()
.context(format!("Parsing bytes from body of response for {}", url))
}),
download_as_mp3: Box::new(|url| { download_as_mp3: Box::new(|url| {
let cmd = "yt-dlp"; let cmd = "yt-dlp";
// println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href);
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(url) .arg(url)
.output()?; .output()
.with_context(|| {
format!(
"Running: {} --extract-audio --audio-format mp3 {}",
cmd, url
)
})?;
if !output.stderr.is_empty() { if !output.stderr.is_empty() {
eprintln!("Error: {}", String::from_utf8(output.stderr)?); eprintln!(
println!("{}", String::from_utf8(output.stdout)?); "Error: {}",
String::from_utf8(output.stderr).context("Parsing stderr")?
);
println!(
"{}",
String::from_utf8(output.stdout).context("Parsing stdout")?
);
} }
Ok(()) Ok(())
}), }),

View file

@ -61,9 +61,10 @@ pub fn mock_network_fetch_as_bytes_with_rss_entries(
pub fn mock_file_open(real_paths: HashMap<String, String>) -> FileOpenFn { pub fn mock_file_open(real_paths: HashMap<String, String>) -> FileOpenFn {
Box::new(move |path: &str| { Box::new(move |path: &str| {
if let Some(real_path) = real_paths.get(&path.to_string()) { if let Some(real_path) = real_paths.get(&path.to_string()) {
Ok(File::open(real_path).with_context(|| { Ok(File::open(real_path)
format!("test_utils/mock_file_open: path={path}, real_path={real_path}, path_map=[{:?}]", real_paths) .context(format!(
})?) "test_utils/mock_file_open: path={path}, real_path={real_path}, path_map=[{:?}]",
real_paths))?)
} else { } else {
Err(anyhow!( Err(anyhow!(
"Not implemented: test_utils/mock_file_open: {}", "Not implemented: test_utils/mock_file_open: {}",