From 97ce2b374ff355313690e9b3ebf7d8cbb6c39a3a Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:28:38 +0100 Subject: [PATCH 01/14] add clap dependency --- Cargo.lock | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + 2 files changed, 101 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 75d0f97..1fe5cc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,55 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "atom_syndication" version = "0.12.1" @@ -127,6 +176,39 @@ dependencies = [ "num-traits", ] +[[package]] +name = "clap" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "core-foundation" version = "0.9.3" @@ -580,6 +662,17 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + [[package]] name = "itoa" version = "1.0.9" @@ -921,6 +1014,7 @@ name = "podal" version = "0.1.0" dependencies = [ "atom_syndication", + "clap", "reqwest", "scraper", ] @@ -1425,6 +1519,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 5e00eca..1705009 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" atom_syndication = "0.12.1" reqwest = { version = "0.11.18", features = ["json", "blocking"] } scraper = "0.17.1" +clap = "4.3.19" From 43be49910b24f8b7038c6baeb645f1e17fe5a19e Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:33:41 +0100 Subject: [PATCH 02/14] split main.rs into lib.rs and main.rs --- src/lib.rs | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 163 +--------------------------------------------------- 2 files changed, 165 insertions(+), 161 deletions(-) create mode 100644 src/lib.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..60f9ca5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,163 @@ +// https://www.phind.com/agent?cache=clke9xk39001cmj085upzho1t + +use std::{fmt::Display, fs::File, string::FromUtf8Error}; + +use atom_syndication::{Entry, Feed, Link}; + +// +// ERRORS +// +#[derive(Debug)] +pub struct Error { + details: String, +} +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.details.to_string().as_str()) + } +} +impl From for Error { + fn from(value: FromUtf8Error) -> Self { + Self { + details: value.to_string(), + } + } +} +impl From for Error { + fn from(details: String) -> Self { + Self { details } + } +} +impl From for Error { + fn from(value: std::io::Error) -> Self { + Self { + details: value.to_string(), + } + } +} +impl From for Error { + fn from(value: atom_syndication::Error) -> Self { + Self { + details: value.to_string(), + } + } +} +impl From for Error { + fn from(value: reqwest::Error) -> Self { + Self { + details: value.to_string(), + } + } +} + +// +// RESULTS +// +pub type Result = std::result::Result; + +// +// MAIN +// +pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { + for channel_name in lines_from(subscriptions)? { + let channel_name = channel_name?; + println!("Channel: {}", channel_name); + let feed_url = get_feed_url(site, &channel_name)?; + for entry in get_feed(feed_url)?.entries() { + if let Some(link) = get_link(entry) { + if !is_already_downloaded(&link, history)? { + println!("Downloading {}: {}", &channel_name, entry.title().as_str()); + download_audio(&link)?; + mark_as_downloaded(&link, history)?; + } + } + } + } + Ok(()) +} + +fn get_feed_url(site: &str, channel_name: &str) -> Result { + if let Some(channel_prefix) = channel_name.chars().next() { + if channel_prefix != '@' { + return Err(format!("Channel Name must begin with an '@': {}", channel_name).into()); + } + } + let channel_url = format!("{}{}", site, channel_name); + let response = reqwest::blocking::get(channel_url)?; + let rss_url = scraper::Html::parse_document(&response.text()?) + .select(&scraper::Selector::parse("link[title='RSS']").unwrap()) + .next() + .unwrap() + .value() + .attr("href") + .unwrap() + .to_string(); + Ok(rss_url) +} + +fn get_link(item: &Entry) -> Option { + item.links().get(0).cloned() +} + +// read list of rss feed URLs from file 'feeds.txt' +fn lines_from(file_name: &str) -> Result>> { + use std::io::{BufRead, BufReader}; + + let file = File::open(file_name)?; + let reader = BufReader::new(file); + Ok(reader.lines()) +} + +// fetch the RSS feed +fn get_feed(url: String) -> Result { + let content = reqwest::blocking::get(url)?.bytes()?; + let channel = Feed::read_from(&content[..])?; + Ok(channel) +} + +fn is_already_downloaded(link: &Link, file_name: &str) -> Result { + use std::io::{BufRead, BufReader}; + + if let Ok(file) = File::open(file_name) { + let reader = BufReader::new(file); + for line in reader.lines() { + if line? == link.href { + return Ok(true); // is already downloaded + } + } + } + Ok(false) // is not already downloaded +} + +fn download_audio(link: &Link) -> Result<()> { + use std::process::Command; + + let cmd = "yt-dlp"; + // println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href); + let output = Command::new(cmd) + .arg("--extract-audio") + .arg("--audio-format") + .arg("mp3") + .arg(&link.href) + .output()?; + if !output.stderr.is_empty() { + eprintln!("Error: {}", String::from_utf8(output.stderr)?); + println!("{}", String::from_utf8(output.stdout)?); + } + Ok(()) +} + +fn mark_as_downloaded(link: &Link, file_name: &str) -> Result<()> { + use std::fs::OpenOptions; + use std::io::prelude::*; + + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(file_name) + .unwrap(); + + writeln!(file, "{}", link.href)?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 46cef06..83b79ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,170 +1,11 @@ -// https://www.phind.com/agent?cache=clke9xk39001cmj085upzho1t - -use std::{fmt::Display, fs::File, string::FromUtf8Error}; - -use atom_syndication::{Entry, Feed, Link}; - -// -// ERRORS -// -#[derive(Debug)] -struct Error { - details: String, -} -impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.details.to_string().as_str()) - } -} -impl From for Error { - fn from(value: FromUtf8Error) -> Self { - Self { - details: value.to_string(), - } - } -} -impl From for Error { - fn from(details: String) -> Self { - Self { details } - } -} -impl From for Error { - fn from(value: std::io::Error) -> Self { - Self { - details: value.to_string(), - } - } -} -impl From for Error { - fn from(value: atom_syndication::Error) -> Self { - Self { - details: value.to_string(), - } - } -} -impl From for Error { - fn from(value: reqwest::Error) -> Self { - Self { - details: value.to_string(), - } - } -} - -// -// RESULTS -// -type Result = std::result::Result; - -// -// MAIN -// -fn main() -> Result<()> { +fn main() -> podal::Result<()> { println!("Podal"); let subscriptions = "subscriptions.txt"; let history = "downloaded.txt"; let site = "https://www.youtube.com/"; - for channel_name in lines_from(subscriptions)? { - let channel_name = channel_name?; - println!("Channel: {}", channel_name); - let feed_url = get_feed_url(site, &channel_name)?; - for entry in get_feed(feed_url)?.entries() { - if let Some(link) = get_link(entry) { - if !is_already_downloaded(&link, history)? { - println!("Downloading {}: {}", &channel_name, entry.title().as_str()); - download_audio(&link)?; - mark_as_downloaded(&link, history)?; - } - } - } - } + podal::run(subscriptions, history, site)?; println!("Done"); Ok(()) } - -fn get_feed_url(site: &str, channel_name: &str) -> Result { - if let Some(channel_prefix) = channel_name.chars().next() { - if channel_prefix != '@' { - return Err(format!("Channel Name must begin with an '@': {}", channel_name).into()); - } - } - let channel_url = format!("{}{}", site, channel_name); - let response = reqwest::blocking::get(channel_url)?; - let rss_url = scraper::Html::parse_document(&response.text()?) - .select(&scraper::Selector::parse("link[title='RSS']").unwrap()) - .next() - .unwrap() - .value() - .attr("href") - .unwrap() - .to_string(); - Ok(rss_url) -} - -fn get_link(item: &Entry) -> Option { - item.links().get(0).cloned() -} - -// read list of rss feed URLs from file 'feeds.txt' -fn lines_from(file_name: &str) -> Result>> { - use std::io::{BufRead, BufReader}; - - let file = File::open(file_name)?; - let reader = BufReader::new(file); - Ok(reader.lines()) -} - -// fetch the RSS feed -fn get_feed(url: String) -> Result { - let content = reqwest::blocking::get(url)?.bytes()?; - let channel = Feed::read_from(&content[..])?; - Ok(channel) -} - -fn is_already_downloaded(link: &Link, file_name: &str) -> Result { - use std::io::{BufRead, BufReader}; - - if let Ok(file) = File::open(file_name) { - let reader = BufReader::new(file); - for line in reader.lines() { - if line? == link.href { - return Ok(true); // is already downloaded - } - } - } - Ok(false) // is not already downloaded -} - -fn download_audio(link: &Link) -> Result<()> { - use std::process::Command; - - let cmd = "yt-dlp"; - // println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href); - let output = Command::new(cmd) - .arg("--extract-audio") - .arg("--audio-format") - .arg("mp3") - .arg(&link.href) - .output()?; - if !output.stderr.is_empty() { - eprintln!("Error: {}", String::from_utf8(output.stderr)?); - println!("{}", String::from_utf8(output.stdout)?); - } - Ok(()) -} - -fn mark_as_downloaded(link: &Link, file_name: &str) -> Result<()> { - use std::fs::OpenOptions; - use std::io::prelude::*; - - let mut file = OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(file_name) - .unwrap(); - - writeln!(file, "{}", link.href)?; - Ok(()) -} From ec20864dac6ef1ab05b52e6bf50fe48a58f75d28 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:36:08 +0100 Subject: [PATCH 03/14] extract errors.rs --- src/errors.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 49 +++---------------------------------------------- 2 files changed, 47 insertions(+), 46 deletions(-) create mode 100644 src/errors.rs diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..1e2baff --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,44 @@ +use std::{fmt::Display, string::FromUtf8Error}; + +#[derive(Debug)] +pub struct Error { + details: String, +} +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.details.to_string().as_str()) + } +} +impl From for Error { + fn from(value: FromUtf8Error) -> Self { + Self { + details: value.to_string(), + } + } +} +impl From for Error { + fn from(details: String) -> Self { + Self { details } + } +} +impl From for Error { + fn from(value: std::io::Error) -> Self { + Self { + details: value.to_string(), + } + } +} +impl From for Error { + fn from(value: atom_syndication::Error) -> Self { + Self { + details: value.to_string(), + } + } +} +impl From for Error { + fn from(value: reqwest::Error) -> Self { + Self { + details: value.to_string(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 60f9ca5..ad4e909 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,54 +1,11 @@ // https://www.phind.com/agent?cache=clke9xk39001cmj085upzho1t -use std::{fmt::Display, fs::File, string::FromUtf8Error}; +use std::fs::File; use atom_syndication::{Entry, Feed, Link}; -// -// ERRORS -// -#[derive(Debug)] -pub struct Error { - details: String, -} -impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.details.to_string().as_str()) - } -} -impl From for Error { - fn from(value: FromUtf8Error) -> Self { - Self { - details: value.to_string(), - } - } -} -impl From for Error { - fn from(details: String) -> Self { - Self { details } - } -} -impl From for Error { - fn from(value: std::io::Error) -> Self { - Self { - details: value.to_string(), - } - } -} -impl From for Error { - fn from(value: atom_syndication::Error) -> Self { - Self { - details: value.to_string(), - } - } -} -impl From for Error { - fn from(value: reqwest::Error) -> Self { - Self { - details: value.to_string(), - } - } -} +mod errors; +use errors::Error; // // RESULTS From 507447f6ad2e50aea6c5ade75ce8bffbd13a33be Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:46:47 +0100 Subject: [PATCH 04/14] extract prelude and feed::find --- src/feed/find.rs | 20 ++++++++++++++++++++ src/feed/mod.rs | 3 +++ src/lib.rs | 32 ++++---------------------------- src/main.rs | 4 +++- src/prelude.rs | 3 +++ 5 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 src/feed/find.rs create mode 100644 src/feed/mod.rs create mode 100644 src/prelude.rs diff --git a/src/feed/find.rs b/src/feed/find.rs new file mode 100644 index 0000000..da4ccd6 --- /dev/null +++ b/src/feed/find.rs @@ -0,0 +1,20 @@ +use crate::prelude::*; + +pub fn get_feed_url(site: &str, channel_name: &str) -> Result { + if let Some(channel_prefix) = channel_name.chars().next() { + if channel_prefix != '@' { + return Err(format!("Channel Name must begin with an '@': {}", channel_name).into()); + } + } + let channel_url = format!("{}{}", site, channel_name); + let response = reqwest::blocking::get(channel_url)?; + let rss_url = scraper::Html::parse_document(&response.text()?) + .select(&scraper::Selector::parse("link[title='RSS']").unwrap()) + .next() + .unwrap() + .value() + .attr("href") + .unwrap() + .to_string(); + Ok(rss_url) +} diff --git a/src/feed/mod.rs b/src/feed/mod.rs new file mode 100644 index 0000000..385ab7d --- /dev/null +++ b/src/feed/mod.rs @@ -0,0 +1,3 @@ +mod find; + +pub use find::get_feed_url; diff --git a/src/lib.rs b/src/lib.rs index ad4e909..3702f3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,21 +5,16 @@ use std::fs::File; use atom_syndication::{Entry, Feed, Link}; mod errors; -use errors::Error; +mod feed; +pub mod prelude; -// -// RESULTS -// -pub type Result = std::result::Result; +use prelude::*; -// -// MAIN -// pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { for channel_name in lines_from(subscriptions)? { let channel_name = channel_name?; println!("Channel: {}", channel_name); - let feed_url = get_feed_url(site, &channel_name)?; + let feed_url = feed::get_feed_url(site, &channel_name)?; for entry in get_feed(feed_url)?.entries() { if let Some(link) = get_link(entry) { if !is_already_downloaded(&link, history)? { @@ -33,25 +28,6 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { Ok(()) } -fn get_feed_url(site: &str, channel_name: &str) -> Result { - if let Some(channel_prefix) = channel_name.chars().next() { - if channel_prefix != '@' { - return Err(format!("Channel Name must begin with an '@': {}", channel_name).into()); - } - } - let channel_url = format!("{}{}", site, channel_name); - let response = reqwest::blocking::get(channel_url)?; - let rss_url = scraper::Html::parse_document(&response.text()?) - .select(&scraper::Selector::parse("link[title='RSS']").unwrap()) - .next() - .unwrap() - .value() - .attr("href") - .unwrap() - .to_string(); - Ok(rss_url) -} - fn get_link(item: &Entry) -> Option { item.links().get(0).cloned() } diff --git a/src/main.rs b/src/main.rs index 83b79ec..10e10e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ -fn main() -> podal::Result<()> { +use podal::prelude::*; + +fn main() -> Result<()> { println!("Podal"); let subscriptions = "subscriptions.txt"; let history = "downloaded.txt"; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..476ec26 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,3 @@ +use crate::errors::Error; + +pub type Result = std::result::Result; From 0b6903404a46b7c8ca017f8b30e5fc75de03df59 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:50:00 +0100 Subject: [PATCH 05/14] extract feed::get --- src/feed/get.rs | 9 +++++++++ src/feed/mod.rs | 2 ++ src/lib.rs | 11 ++--------- 3 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 src/feed/get.rs diff --git a/src/feed/get.rs b/src/feed/get.rs new file mode 100644 index 0000000..5288c6b --- /dev/null +++ b/src/feed/get.rs @@ -0,0 +1,9 @@ +use atom_syndication::Feed; + +use crate::prelude::*; + +pub fn get_feed(url: String) -> Result { + let content = reqwest::blocking::get(url)?.bytes()?; + let channel = Feed::read_from(&content[..])?; + Ok(channel) +} diff --git a/src/feed/mod.rs b/src/feed/mod.rs index 385ab7d..1b9943a 100644 --- a/src/feed/mod.rs +++ b/src/feed/mod.rs @@ -1,3 +1,5 @@ mod find; +mod get; pub use find::get_feed_url; +pub use get::get_feed; diff --git a/src/lib.rs b/src/lib.rs index 3702f3a..ca157c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ use std::fs::File; -use atom_syndication::{Entry, Feed, Link}; +use atom_syndication::{Entry, Link}; mod errors; mod feed; @@ -15,7 +15,7 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { let channel_name = channel_name?; println!("Channel: {}", channel_name); let feed_url = feed::get_feed_url(site, &channel_name)?; - for entry in get_feed(feed_url)?.entries() { + for entry in feed::get_feed(feed_url)?.entries() { if let Some(link) = get_link(entry) { if !is_already_downloaded(&link, history)? { println!("Downloading {}: {}", &channel_name, entry.title().as_str()); @@ -41,13 +41,6 @@ fn lines_from(file_name: &str) -> Result Result { - let content = reqwest::blocking::get(url)?.bytes()?; - let channel = Feed::read_from(&content[..])?; - Ok(channel) -} - fn is_already_downloaded(link: &Link, file_name: &str) -> Result { use std::io::{BufRead, BufReader}; From 05f72589782000cf181bbacf2141ed5dc9b3498a Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:50:31 +0100 Subject: [PATCH 06/14] inline get_link function --- src/lib.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ca157c4..f918b52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ use std::fs::File; -use atom_syndication::{Entry, Link}; +use atom_syndication::Link; mod errors; mod feed; @@ -16,7 +16,7 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { println!("Channel: {}", channel_name); let feed_url = feed::get_feed_url(site, &channel_name)?; for entry in feed::get_feed(feed_url)?.entries() { - if let Some(link) = get_link(entry) { + if let Some(link) = entry.links().get(0).cloned() { if !is_already_downloaded(&link, history)? { println!("Downloading {}: {}", &channel_name, entry.title().as_str()); download_audio(&link)?; @@ -28,10 +28,6 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { Ok(()) } -fn get_link(item: &Entry) -> Option { - item.links().get(0).cloned() -} - // read list of rss feed URLs from file 'feeds.txt' fn lines_from(file_name: &str) -> Result>> { use std::io::{BufRead, BufReader}; From 8b7c88e85c74107e929ae7434a34369087196bab Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:53:26 +0100 Subject: [PATCH 07/14] extract history::find --- src/history/find.rs | 17 +++++++++++++++++ src/history/mod.rs | 3 +++ src/lib.rs | 17 ++--------------- 3 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 src/history/find.rs create mode 100644 src/history/mod.rs diff --git a/src/history/find.rs b/src/history/find.rs new file mode 100644 index 0000000..254c41f --- /dev/null +++ b/src/history/find.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +use atom_syndication::Link; +use std::fs::File; +use std::io::{BufRead, BufReader}; + +pub fn is_already_downloaded(link: &Link, file_name: &str) -> Result { + if let Ok(file) = File::open(file_name) { + let reader = BufReader::new(file); + for line in reader.lines() { + if line? == link.href { + return Ok(true); // is already downloaded + } + } + } + Ok(false) // is not already downloaded +} diff --git a/src/history/mod.rs b/src/history/mod.rs new file mode 100644 index 0000000..8bdd517 --- /dev/null +++ b/src/history/mod.rs @@ -0,0 +1,3 @@ +mod find; + +pub use find::is_already_downloaded; diff --git a/src/lib.rs b/src/lib.rs index f918b52..3f486ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ use atom_syndication::Link; mod errors; mod feed; +mod history; pub mod prelude; use prelude::*; @@ -17,7 +18,7 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { let feed_url = feed::get_feed_url(site, &channel_name)?; for entry in feed::get_feed(feed_url)?.entries() { if let Some(link) = entry.links().get(0).cloned() { - if !is_already_downloaded(&link, history)? { + if !history::is_already_downloaded(&link, history)? { println!("Downloading {}: {}", &channel_name, entry.title().as_str()); download_audio(&link)?; mark_as_downloaded(&link, history)?; @@ -37,20 +38,6 @@ fn lines_from(file_name: &str) -> Result Result { - use std::io::{BufRead, BufReader}; - - if let Ok(file) = File::open(file_name) { - let reader = BufReader::new(file); - for line in reader.lines() { - if line? == link.href { - return Ok(true); // is already downloaded - } - } - } - Ok(false) // is not already downloaded -} - fn download_audio(link: &Link) -> Result<()> { use std::process::Command; From a60999785b5375b6302a313a8f872f122609ec29 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:55:02 +0100 Subject: [PATCH 08/14] extract history::add --- src/history/add.rs | 17 +++++++++++++++++ src/history/mod.rs | 2 ++ src/lib.rs | 17 +---------------- 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 src/history/add.rs diff --git a/src/history/add.rs b/src/history/add.rs new file mode 100644 index 0000000..e186644 --- /dev/null +++ b/src/history/add.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +use atom_syndication::Link; +use std::fs::OpenOptions; +use std::io::prelude::*; + +pub fn mark_as_downloaded(link: &Link, file_name: &str) -> Result<()> { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(file_name) + .unwrap(); + + writeln!(file, "{}", link.href)?; + Ok(()) +} diff --git a/src/history/mod.rs b/src/history/mod.rs index 8bdd517..87d3186 100644 --- a/src/history/mod.rs +++ b/src/history/mod.rs @@ -1,3 +1,5 @@ +mod add; mod find; +pub use add::mark_as_downloaded; pub use find::is_already_downloaded; diff --git a/src/lib.rs b/src/lib.rs index 3f486ad..c98b471 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { if !history::is_already_downloaded(&link, history)? { println!("Downloading {}: {}", &channel_name, entry.title().as_str()); download_audio(&link)?; - mark_as_downloaded(&link, history)?; + history::mark_as_downloaded(&link, history)?; } } } @@ -55,18 +55,3 @@ fn download_audio(link: &Link) -> Result<()> { } Ok(()) } - -fn mark_as_downloaded(link: &Link, file_name: &str) -> Result<()> { - use std::fs::OpenOptions; - use std::io::prelude::*; - - let mut file = OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(file_name) - .unwrap(); - - writeln!(file, "{}", link.href)?; - Ok(()) -} From 13600e092703aa8801fafa51db93e207214df260 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:57:19 +0100 Subject: [PATCH 09/14] extract subscriptions --- src/lib.rs | 14 ++------------ src/subscriptions.rs | 10 ++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 src/subscriptions.rs diff --git a/src/lib.rs b/src/lib.rs index c98b471..e698fa7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,17 @@ // https://www.phind.com/agent?cache=clke9xk39001cmj085upzho1t -use std::fs::File; - use atom_syndication::Link; mod errors; mod feed; mod history; pub mod prelude; +mod subscriptions; use prelude::*; pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { - for channel_name in lines_from(subscriptions)? { + for channel_name in subscriptions::lines_from(subscriptions)? { let channel_name = channel_name?; println!("Channel: {}", channel_name); let feed_url = feed::get_feed_url(site, &channel_name)?; @@ -29,15 +28,6 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { Ok(()) } -// read list of rss feed URLs from file 'feeds.txt' -fn lines_from(file_name: &str) -> Result>> { - use std::io::{BufRead, BufReader}; - - let file = File::open(file_name)?; - let reader = BufReader::new(file); - Ok(reader.lines()) -} - fn download_audio(link: &Link) -> Result<()> { use std::process::Command; diff --git a/src/subscriptions.rs b/src/subscriptions.rs new file mode 100644 index 0000000..a9f7cde --- /dev/null +++ b/src/subscriptions.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; + +use std::fs::File; +use std::io::{BufRead, BufReader, Lines}; + +pub fn lines_from(file_name: &str) -> Result>> { + let file = File::open(file_name)?; + let reader = BufReader::new(file); + Ok(reader.lines()) +} From e1ea5a81ecccba278d4bd8568be090f92304b1d8 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 10:58:56 +0100 Subject: [PATCH 10/14] extract fetch --- src/fetch.rs | 21 +++++++++++++++++++++ src/lib.rs | 25 ++----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 src/fetch.rs diff --git a/src/fetch.rs b/src/fetch.rs new file mode 100644 index 0000000..38fd8ff --- /dev/null +++ b/src/fetch.rs @@ -0,0 +1,21 @@ +use crate::prelude::*; + +use atom_syndication::Link; + +pub fn download_audio(link: &Link) -> Result<()> { + use std::process::Command; + + let cmd = "yt-dlp"; + // println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href); + let output = Command::new(cmd) + .arg("--extract-audio") + .arg("--audio-format") + .arg("mp3") + .arg(&link.href) + .output()?; + if !output.stderr.is_empty() { + eprintln!("Error: {}", String::from_utf8(output.stderr)?); + println!("{}", String::from_utf8(output.stdout)?); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index e698fa7..15b61f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,6 @@ -// https://www.phind.com/agent?cache=clke9xk39001cmj085upzho1t - -use atom_syndication::Link; - mod errors; mod feed; +mod fetch; mod history; pub mod prelude; mod subscriptions; @@ -19,7 +16,7 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { if let Some(link) = entry.links().get(0).cloned() { if !history::is_already_downloaded(&link, history)? { println!("Downloading {}: {}", &channel_name, entry.title().as_str()); - download_audio(&link)?; + fetch::download_audio(&link)?; history::mark_as_downloaded(&link, history)?; } } @@ -27,21 +24,3 @@ pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { } Ok(()) } - -fn download_audio(link: &Link) -> Result<()> { - use std::process::Command; - - let cmd = "yt-dlp"; - // println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href); - let output = Command::new(cmd) - .arg("--extract-audio") - .arg("--audio-format") - .arg("mp3") - .arg(&link.href) - .output()?; - if !output.stderr.is_empty() { - eprintln!("Error: {}", String::from_utf8(output.stderr)?); - println!("{}", String::from_utf8(output.stdout)?); - } - Ok(()) -} From 17a92e45e49775e6a40d15ebbd73068f4514e597 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 14:47:33 +0100 Subject: [PATCH 11/14] provide feed find/get functions as parameters from main --- src/feed/find.rs | 2 +- src/feed/get.rs | 2 +- src/feed/mod.rs | 11 +++++++++-- src/lib.rs | 14 ++++++++++---- src/main.rs | 8 +++++++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/feed/find.rs b/src/feed/find.rs index da4ccd6..b735e05 100644 --- a/src/feed/find.rs +++ b/src/feed/find.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -pub fn get_feed_url(site: &str, channel_name: &str) -> Result { +pub fn find(site: &str, channel_name: &str) -> Result { if let Some(channel_prefix) = channel_name.chars().next() { if channel_prefix != '@' { return Err(format!("Channel Name must begin with an '@': {}", channel_name).into()); diff --git a/src/feed/get.rs b/src/feed/get.rs index 5288c6b..dad28b0 100644 --- a/src/feed/get.rs +++ b/src/feed/get.rs @@ -2,7 +2,7 @@ use atom_syndication::Feed; use crate::prelude::*; -pub fn get_feed(url: String) -> Result { +pub fn get(url: &str) -> Result { let content = reqwest::blocking::get(url)?.bytes()?; let channel = Feed::read_from(&content[..])?; Ok(channel) diff --git a/src/feed/mod.rs b/src/feed/mod.rs index 1b9943a..8b43067 100644 --- a/src/feed/mod.rs +++ b/src/feed/mod.rs @@ -1,5 +1,12 @@ +use crate::prelude::*; + mod find; mod get; -pub use find::get_feed_url; -pub use get::get_feed; +pub use find::find; +pub use get::get; + +type Feed = atom_syndication::Feed; + +pub type FeedFind = fn(&str, &str) -> Result; +pub type FeedGet = fn(&str) -> Result; diff --git a/src/lib.rs b/src/lib.rs index 15b61f6..a3447e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ mod errors; -mod feed; +pub mod feed; mod fetch; mod history; pub mod prelude; @@ -7,12 +7,18 @@ mod subscriptions; use prelude::*; -pub fn run(subscriptions: &str, history: &str, site: &str) -> Result<()> { +pub fn run( + subscriptions: &str, + history: &str, + site: &str, + feed_find: feed::FeedFind, + feed_get: feed::FeedGet, +) -> Result<()> { for channel_name in subscriptions::lines_from(subscriptions)? { let channel_name = channel_name?; println!("Channel: {}", channel_name); - let feed_url = feed::get_feed_url(site, &channel_name)?; - for entry in feed::get_feed(feed_url)?.entries() { + let feed_url = feed_find(site, &channel_name)?; + for entry in feed_get(&feed_url)?.entries() { if let Some(link) = entry.links().get(0).cloned() { if !history::is_already_downloaded(&link, history)? { println!("Downloading {}: {}", &channel_name, entry.title().as_str()); diff --git a/src/main.rs b/src/main.rs index 10e10e3..4ebdc95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,13 @@ fn main() -> Result<()> { let history = "downloaded.txt"; let site = "https://www.youtube.com/"; - podal::run(subscriptions, history, site)?; + podal::run( + subscriptions, + history, + site, + podal::feed::find, + podal::feed::get, + )?; println!("Done"); Ok(()) From 65156db75ed7198a470dbd8bccacbf1655f0d717 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 14:56:59 +0100 Subject: [PATCH 12/14] provide history find/add and fetch download as parameters to main --- src/fetch.rs | 5 +++-- src/history/add.rs | 2 +- src/history/find.rs | 2 +- src/history/mod.rs | 11 +++++++++-- src/lib.rs | 20 +++++++++++++------- src/main.rs | 3 +++ 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/fetch.rs b/src/fetch.rs index 38fd8ff..1f95f96 100644 --- a/src/fetch.rs +++ b/src/fetch.rs @@ -1,10 +1,11 @@ use crate::prelude::*; use atom_syndication::Link; +use std::process::Command; -pub fn download_audio(link: &Link) -> Result<()> { - use std::process::Command; +pub type FetchDownload = fn(&Link) -> Result<()>; +pub fn download(link: &Link) -> Result<()> { let cmd = "yt-dlp"; // println!("{} --extract-audio --audio-format mp3 {}", cmd, &link.href); let output = Command::new(cmd) diff --git a/src/history/add.rs b/src/history/add.rs index e186644..e904004 100644 --- a/src/history/add.rs +++ b/src/history/add.rs @@ -4,7 +4,7 @@ use atom_syndication::Link; use std::fs::OpenOptions; use std::io::prelude::*; -pub fn mark_as_downloaded(link: &Link, file_name: &str) -> Result<()> { +pub fn add(link: &Link, file_name: &str) -> Result<()> { let mut file = OpenOptions::new() .write(true) .append(true) diff --git a/src/history/find.rs b/src/history/find.rs index 254c41f..5b760b8 100644 --- a/src/history/find.rs +++ b/src/history/find.rs @@ -4,7 +4,7 @@ use atom_syndication::Link; use std::fs::File; use std::io::{BufRead, BufReader}; -pub fn is_already_downloaded(link: &Link, file_name: &str) -> Result { +pub fn find(link: &Link, file_name: &str) -> Result { if let Ok(file) = File::open(file_name) { let reader = BufReader::new(file); for line in reader.lines() { diff --git a/src/history/mod.rs b/src/history/mod.rs index 87d3186..e36428d 100644 --- a/src/history/mod.rs +++ b/src/history/mod.rs @@ -1,5 +1,12 @@ +use crate::prelude::*; + mod add; mod find; -pub use add::mark_as_downloaded; -pub use find::is_already_downloaded; +pub use add::add; +pub use find::find; + +type Link = atom_syndication::Link; + +pub type HistoryFind = fn(&Link, &str) -> Result; +pub type HistoryAdd = fn(&Link, &str) -> Result<()>; diff --git a/src/lib.rs b/src/lib.rs index a3447e0..b7bee6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,24 @@ mod errors; pub mod feed; -mod fetch; -mod history; +pub mod fetch; +pub mod history; pub mod prelude; mod subscriptions; +use feed::{FeedFind, FeedGet}; +use fetch::FetchDownload; +use history::{HistoryAdd, HistoryFind}; use prelude::*; pub fn run( subscriptions: &str, history: &str, site: &str, - feed_find: feed::FeedFind, - feed_get: feed::FeedGet, + feed_find: FeedFind, + feed_get: FeedGet, + history_find: HistoryFind, + history_add: HistoryAdd, + fetch_download: FetchDownload, ) -> Result<()> { for channel_name in subscriptions::lines_from(subscriptions)? { let channel_name = channel_name?; @@ -20,10 +26,10 @@ pub fn run( let feed_url = feed_find(site, &channel_name)?; for entry in feed_get(&feed_url)?.entries() { if let Some(link) = entry.links().get(0).cloned() { - if !history::is_already_downloaded(&link, history)? { + if !history_find(&link, history)? { println!("Downloading {}: {}", &channel_name, entry.title().as_str()); - fetch::download_audio(&link)?; - history::mark_as_downloaded(&link, history)?; + fetch_download(&link)?; + history_add(&link, history)?; } } } diff --git a/src/main.rs b/src/main.rs index 4ebdc95..3e4a741 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,9 @@ fn main() -> Result<()> { site, podal::feed::find, podal::feed::get, + podal::history::find, + podal::history::add, + podal::fetch::download, )?; println!("Done"); From 6d967f5eae821ba2bc7c923671db0500cc1784c2 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 16:57:08 +0100 Subject: [PATCH 13/14] Add tests for loading subscriptions file --- .gitignore | 3 +- Cargo.lock | 1 + Cargo.toml | 3 + src/errors.rs | 9 ++- src/lib.rs | 1 - src/subscriptions.rs | 83 +++++++++++++++++++++++++- test/data/subscriptions-blank-line.txt | 4 ++ test/data/subscriptions-comment.txt | 3 + test/data/subscriptions.txt | 3 + 9 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 test/data/subscriptions-blank-line.txt create mode 100644 test/data/subscriptions-comment.txt create mode 100644 test/data/subscriptions.txt diff --git a/.gitignore b/.gitignore index 2143bde..c233fd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target *.mp3 *.webm -*.txt +/subscriptions.txt +/downloaded.txt diff --git a/Cargo.lock b/Cargo.lock index 1fe5cc9..f20c98c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1017,6 +1017,7 @@ dependencies = [ "clap", "reqwest", "scraper", + "tempfile", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1705009..0c54643 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,6 @@ atom_syndication = "0.12.1" reqwest = { version = "0.11.18", features = ["json", "blocking"] } scraper = "0.17.1" clap = "4.3.19" + +[dev-dependencies] +tempfile = "*" diff --git a/src/errors.rs b/src/errors.rs index 1e2baff..6769040 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,4 +1,4 @@ -use std::{fmt::Display, string::FromUtf8Error}; +use std::{fmt::Display, str::Utf8Error, string::FromUtf8Error}; #[derive(Debug)] pub struct Error { @@ -9,6 +9,13 @@ impl Display for Error { f.write_str(self.details.to_string().as_str()) } } +impl From for Error { + fn from(value: Utf8Error) -> Self { + Self { + details: value.to_string(), + } + } +} impl From for Error { fn from(value: FromUtf8Error) -> Self { Self { diff --git a/src/lib.rs b/src/lib.rs index b7bee6f..ee239ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,6 @@ pub fn run( fetch_download: FetchDownload, ) -> Result<()> { for channel_name in subscriptions::lines_from(subscriptions)? { - let channel_name = channel_name?; println!("Channel: {}", channel_name); let feed_url = feed_find(site, &channel_name)?; for entry in feed_get(&feed_url)?.entries() { diff --git a/src/subscriptions.rs b/src/subscriptions.rs index a9f7cde..5e5a955 100644 --- a/src/subscriptions.rs +++ b/src/subscriptions.rs @@ -1,10 +1,87 @@ use crate::prelude::*; use std::fs::File; -use std::io::{BufRead, BufReader, Lines}; +use std::io::{BufRead, BufReader}; -pub fn lines_from(file_name: &str) -> Result>> { +pub fn lines_from(file_name: &str) -> Result> { let file = File::open(file_name)?; let reader = BufReader::new(file); - Ok(reader.lines()) + let mut lines = vec![]; + for line in reader.lines() { + if let Ok(line) = line { + if line.starts_with('@') { + lines.push(line); + } + } + } + Ok(lines) +} + +#[cfg(test)] +mod tests { + use std::{fs::File, io::Write, str::from_utf8}; + + use tempfile::{tempdir, TempDir}; + + use super::*; + + #[test] + fn can_load_file() -> Result<()> { + //given + let (dir, file_name) = create_text_file( + "subscriptions.txt", + include_bytes!("../test/data/subscriptions.txt"), + )?; + + //when + let result = lines_from(&file_name)?; + + //then + drop(dir); + assert_eq!(result, ["@sub1", "@sub2", "@sub3"]); + Ok(()) + } + + #[test] + fn ignores_blank_lines() -> Result<()> { + //given + let (dir, file_name) = create_text_file( + "subscriptions.txt", + include_bytes!("../test/data/subscriptions-blank-line.txt"), + )?; + + //when + let result = lines_from(&file_name)?; + + //then + drop(dir); + assert_eq!(result, ["@sub1", "@sub2", "@sub3"]); + Ok(()) + } + + #[test] + fn ignores_comments() -> Result<()> { + //given + let (dir, file_name) = create_text_file( + "subscriptions.txt", + include_bytes!("../test/data/subscriptions-comment.txt"), + )?; + + //when + let result = lines_from(&file_name)?; + + //then + drop(dir); + assert_eq!(result, ["@sub1", "@sub3"]); + Ok(()) + } + + fn create_text_file(name: &str, data: &[u8]) -> Result<(TempDir, String)> { + let data = from_utf8(data)?; + let dir = tempdir()?; + let filename = format!("{}", &dir.path().join(name).display()); + let file = File::create(&filename)?; + write!(&file, "{data}")?; + Ok((dir, filename)) + } } diff --git a/test/data/subscriptions-blank-line.txt b/test/data/subscriptions-blank-line.txt new file mode 100644 index 0000000..ef718e3 --- /dev/null +++ b/test/data/subscriptions-blank-line.txt @@ -0,0 +1,4 @@ +@sub1 + +@sub2 +@sub3 diff --git a/test/data/subscriptions-comment.txt b/test/data/subscriptions-comment.txt new file mode 100644 index 0000000..851ac8a --- /dev/null +++ b/test/data/subscriptions-comment.txt @@ -0,0 +1,3 @@ +@sub1 +#@sub2 +@sub3 diff --git a/test/data/subscriptions.txt b/test/data/subscriptions.txt new file mode 100644 index 0000000..a1714b6 --- /dev/null +++ b/test/data/subscriptions.txt @@ -0,0 +1,3 @@ +@sub1 +@sub2 +@sub3 From 4a19470712026258cc03e97c2c1a58695a9d7347 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 25 Jul 2023 16:58:17 +0100 Subject: [PATCH 14/14] add woodpecker config --- .woodpecker.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .woodpecker.yml diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..98731e8 --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,9 @@ +pipeline: + build: + image: rust + commands: + - rustup component add rustfmt clippy + - cargo --version + - cargo fmt --check + - cargo clippy --fix -- -Dwarnings -W clippy::nursery -W clippy::unwrap_used -W clippy::expect_used + - cargo test