feat: initial mvp
This commit is contained in:
parent
cd417d2b12
commit
f87d74f780
3 changed files with 2235 additions and 2 deletions
2162
Cargo.lock
generated
Normal file
2162
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -4,3 +4,10 @@ version = "0.1.0"
|
|||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.99"
|
||||
llm_readability = "0.0.11"
|
||||
reqwest = { version = "0.12.23", features = ["blocking", "rustls-tls"], default-features = false }
|
||||
rss = "2.0.12"
|
||||
tokio = { version = "1.47.1", features = ["full"] }
|
||||
urlencoding = "2.1.3"
|
||||
warp = { version = "0.4.2", features = ["server"] }
|
||||
|
|
|
|||
68
src/main.rs
68
src/main.rs
|
|
@ -1,3 +1,67 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use anyhow::Result;
|
||||
use llm_readability::extractor;
|
||||
use reqwest::Url;
|
||||
use rss::Channel;
|
||||
use tokio::task::JoinSet;
|
||||
use warp::Filter;
|
||||
|
||||
async fn get_feed(url: String) -> Result<Channel> {
|
||||
let url = urlencoding::decode(&url)?.into_owned();
|
||||
let content = reqwest::get(url).await?.bytes().await?;
|
||||
let channel = Channel::read_from(&content[..])?;
|
||||
Ok(channel)
|
||||
}
|
||||
|
||||
async fn complete(channel: Channel) -> Result<Box<Channel>> {
|
||||
let items: Vec<rss::Item> = channel.items().into_iter().cloned().collect();
|
||||
|
||||
let mut set = JoinSet::new();
|
||||
for mut item in items {
|
||||
set.spawn(async move {
|
||||
if let Some(link) = item.link.clone() {
|
||||
if let Ok(content) = get_content(link).await {
|
||||
item.set_description(content);
|
||||
};
|
||||
}
|
||||
item
|
||||
});
|
||||
}
|
||||
|
||||
let updated_items = set.join_all().await;
|
||||
|
||||
let mut new_channel = channel.clone();
|
||||
new_channel.set_items(updated_items);
|
||||
Ok(Box::new(new_channel))
|
||||
}
|
||||
|
||||
async fn get_content(link: String) -> Result<String> {
|
||||
let response = reqwest::get(&link).await?;
|
||||
let content = extractor::extract(
|
||||
&mut response.bytes().await?.as_ref(),
|
||||
&Url::parse(link.as_str())?,
|
||||
)?
|
||||
.content;
|
||||
Ok(content)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct CustomReject(anyhow::Error);
|
||||
|
||||
impl warp::reject::Reject for CustomReject {}
|
||||
|
||||
pub(crate) fn custom_reject(error: impl Into<anyhow::Error>) -> warp::Rejection {
|
||||
warp::reject::custom(CustomReject(error.into()))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let path = warp::path!(String)
|
||||
.and_then(|url| async move {
|
||||
let feed = get_feed(url).await.map_err(custom_reject)?;
|
||||
let updated = complete(feed).await.map_err(custom_reject)?;
|
||||
Ok::<String, warp::Rejection>(format!("{}", updated))
|
||||
})
|
||||
.map(|reply| warp::reply::with_header(reply, "Content-Type", "application/rss+xml"));
|
||||
warp::serve(path).run(([127, 0, 0, 1], 3030)).await;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue