use std::sync::Arc; use calibre_db::data::pagination::SortOrder; use poem::{ handler, web::{Data, WithContentType}, IntoResponse, }; use time::OffsetDateTime; use crate::{ app_state::AppState, data::book::Book, handlers::error::HandlerError, opds::{ author::Author, content::Content, entry::Entry, feed::Feed, link::Link, media_type::MediaType, relation::Relation, }, }; fn create_feed( now: OffsetDateTime, self_link: Link, mut additional_links: Vec<Link>, entries: Vec<Entry>, ) -> Feed { let author = Author { name: "Thallian".to_string(), uri: "https://code.vanwa.ch/shu/rusty-library".to_string(), email: None, }; let mut links = vec![ Link { href: "/opds".to_string(), media_type: MediaType::Navigation, rel: Relation::Start, title: Some("Home".to_string()), count: None, }, self_link, ]; links.append(&mut additional_links); Feed { title: "rusty-library".to_string(), id: "rusty:catalog".to_string(), updated: now, icon: "favicon.ico".to_string(), author, links, entries, } } #[handler] pub async fn books(state: Data<&Arc<AppState>>) -> Result<WithContentType<String>, poem::Error> { let books: Vec<Book> = state .calibre .books(u32::MAX.into(), None, &SortOrder::ASC) .map(|x| { x.iter() .filter_map(|y| Book::full_book(y, &state)) .collect() }) .map_err(HandlerError::DataError)?; let entries: Vec<Entry> = books.into_iter().map(Entry::from).collect(); let now = OffsetDateTime::now_utc(); let self_link = Link { href: "/opds/books".to_string(), media_type: MediaType::Navigation, rel: Relation::Myself, title: None, count: None, }; let feed = create_feed(now, self_link, vec![], entries); let xml = feed.as_xml().map_err(HandlerError::OpdsError)?; Ok(xml.with_content_type("application/atom+xml")) } #[handler] pub async fn handler(state: Data<&Arc<AppState>>) -> Result<WithContentType<String>, poem::Error> { let now = OffsetDateTime::now_utc(); let self_link = Link { href: "/opds".to_string(), media_type: MediaType::Navigation, rel: Relation::Myself, title: None, count: None, }; let books_entry = Entry { title: "Books".to_string(), id: "rusty:books".to_string(), updated: now, content: Content { media_type: MediaType::Text, content: "Index of all books".to_string(), }, author: None, links: vec![Link { href: "/opds/books".to_string(), media_type: MediaType::Navigation, rel: Relation::Subsection, title: None, count: None, }], }; let feed = create_feed(now, self_link, vec![], vec![books_entry]); let xml = feed.as_xml().map_err(HandlerError::OpdsError)?; Ok(xml.with_content_type("application/atom+xml")) }