diff --git a/.gitignore b/.gitignore index ac76f28..7e5ca36 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ Cargo.lock *.jpg *.png *.cbz +*.json diff --git a/src/main.rs b/src/main.rs index c4d02a3..4bcc5cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,17 @@ +#![feature(test)] use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; use response_deserializer::{ChapterImages, SearchResult}; use std::fs::File; use std::io::Write; use std::path::Path; +use std::pin::Pin; use zip::write::{SimpleFileOptions, ZipWriter}; use zip::CompressionMethod; mod response_deserializer; mod select; +mod test; mod util; use response_deserializer::{Chapter, Id}; @@ -256,7 +259,7 @@ async fn main() { let zip_file_path = format!("{} - Volume {:0>3}.cbz", title.en, volume); let zip_file_path = Path::new(&zip_file_path); - let zip_file = File::create(&zip_file_path).unwrap(); + let zip_file = File::create(zip_file_path).unwrap(); let mut zip = ZipWriter::new(zip_file); let options = @@ -281,7 +284,7 @@ async fn main() { let zip_file_path = format!("{} - Chapter {:0>3}.cbz", title.en, chapter); let zip_file_path = Path::new(&zip_file_path); - let zip_file = File::create(&zip_file_path).unwrap(); + let zip_file = File::create(zip_file_path).unwrap(); let mut zip = ZipWriter::new(zip_file); let options = @@ -403,7 +406,8 @@ async fn select_manga_from_search( util::CoverSize::W512 => ".512.jpg", }; - let mut entry_futures = Vec::new(); + use std::future::Future; + let mut entry_futures: Vec>>> = Vec::new(); for result in results.data.iter() { let mut entry = Entry::new(result.attributes.title.en.clone()); if let Some(year) = result.attributes.year { @@ -425,25 +429,29 @@ async fn select_manga_from_search( if let Some(cover_data) = &result.relationships[2].attributes { // The lib used for converting to sixel is abysmally slow for larger images, this // should be in a future to allow for multithreaded work - let future = async move { - let image_url = format!( - "https://uploads.mangadex.org/covers/{id}/{}{cover_ex}", - &cover_data.file_name - ); - let data = client - .get(&image_url) - .send() - .await - .unwrap() - .bytes() - .await - .unwrap(); - let result = util::convert_to_sixel(&data); + if config.cover != Some(false) { + let future = async move { + let image_url = format!( + "https://uploads.mangadex.org/covers/{id}/{}{cover_ex}", + &cover_data.file_name + ); + let data = client + .get(&image_url) + .send() + .await + .unwrap() + .bytes() + .await + .unwrap(); + let result = util::convert_to_sixel(&data); - entry.set_image(result); - entry - }; - entry_futures.push(future); + entry.set_image(result); + entry + }; + entry_futures.push(Box::pin(future)); + } else { + entry_futures.push(Box::pin(async move { entry })); + } } } let entries = futures::future::join_all(entry_futures).await; diff --git a/src/response_deserializer.rs b/src/response_deserializer.rs index 4272837..de27706 100644 --- a/src/response_deserializer.rs +++ b/src/response_deserializer.rs @@ -405,7 +405,7 @@ struct ChapterContent { #[serde(rename = "type")] type_name: String, attributes: ChapterAttributesContent, - relationships: Vec, + relationships: Vec, } #[derive(Deserialize, Debug)] @@ -428,7 +428,7 @@ pub struct Chapter { pub id: Id, pub data_type: DataType, pub attributes: ChapterAttributes, - pub relationships: Vec, + pub relationships: Vec, } #[derive(Debug)] @@ -450,7 +450,7 @@ pub struct Manga { pub id: Id, pub data_type: DataType, pub attributes: MangaAttributes, - pub relationships: Vec, + pub relationships: Vec, } #[derive(Deserialize, Debug)] @@ -476,7 +476,7 @@ struct ContentData { #[serde(rename = "type")] type_name: String, attributes: ContentAttributes, - relationships: Vec, + relationships: Vec, } #[derive(Deserialize, Debug)] @@ -504,7 +504,7 @@ struct ContentAttributes { latest_uploaded_chapter: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct ContentCoverAttributes { description: String, @@ -527,13 +527,13 @@ pub struct CoverAttributes { pub version: u32, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] pub struct ContentTag { id: String, #[serde(rename = "type")] type_name: String, attributes: TagAttributes, - relationships: Vec, + relationships: Vec, } #[derive(Debug)] @@ -541,11 +541,11 @@ pub struct Tag { pub id: Id, pub data_type: DataType, pub attributes: TagAttributes, - pub relationships: Vec, + pub relationships: Vec, } -#[derive(Deserialize, Debug, Clone)] -struct ContentRelationShip { +#[derive(Deserialize, Debug)] +struct ContentRelationship { id: String, #[serde(rename = "type")] type_name: String, @@ -554,20 +554,20 @@ struct ContentRelationShip { } #[derive(Debug)] -pub struct ChapterRelationShip { +pub struct ChapterRelationship { id: Id, data_type: DataType, } -#[derive(Debug)] // TODO: Typo: Relationship -pub struct RelationShip { +#[derive(Debug)] +pub struct Relationship { pub id: Id, pub data_type: DataType, pub related: Option, pub attributes: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] pub struct TagAttributes { pub name: TagName, pub description: Description, @@ -575,12 +575,12 @@ pub struct TagAttributes { pub version: u32, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] pub struct TagName { pub en: String, } -#[derive(Deserialize, Debug, Default, Clone)] +#[derive(Deserialize, Debug, Default)] pub struct Links { al: Option, ap: Option, @@ -594,20 +594,20 @@ pub struct Links { engtl: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] pub struct Description { en: Option, ru: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] pub struct AltTitles { en: Option, ja: Option, ru: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] pub struct Titles { pub en: String, } @@ -955,84 +955,28 @@ fn convert_response_to_result( ) -> Result { let response = (search_response.response.as_str()) .try_into() - .map_err(|_| ResponseConversionError::Result(search_response.response.clone()))?; + .map_err(|_| ResponseConversionError::Result(search_response.response))?; let result: ResponseResult = (search_response.result.as_str()) .try_into() - .map_err(|_| ResponseConversionError::Result({ search_response.result.clone() }))?; + .map_err(|_| ResponseConversionError::Result(search_response.result))?; - let data: Vec = search_response - .data - .iter() - .map(|m| { - Ok(Manga { - id: Id(m.id.clone()), - data_type: (m.type_name.as_str()).try_into().map_err(|_| { - ResponseConversionError::AttributeError(AttributeConversionError::DataType( - m.type_name.clone(), - )) - })?, - attributes: convert_attributes(&m.attributes) - .map_err(ResponseConversionError::AttributeError)?, - relationships: m - .relationships - .iter() - .map(|m| { - Ok(RelationShip { - id: Id(m.id.clone()), - data_type: (m.type_name.as_str()).try_into().map_err(|_| { - AttributeConversionError::DataType(m.type_name.clone()) - })?, - attributes: { - if let Some(attributes) = &m.attributes { - Some(CoverAttributes { - created_at: DateTime::parse_from_rfc3339( - &attributes.created_at, - ) - .map_err(|_| { - AttributeConversionError::CreatedAtDateTime( - attributes.created_at.clone(), - ) - })?, - updated_at: DateTime::parse_from_rfc3339( - &attributes.created_at, - ) - .map_err(|_| { - AttributeConversionError::CreatedAtDateTime( - attributes.created_at.clone(), - ) - })?, - // TODO: Something should probably be done here - description: String::new(), - file_name: Id(attributes.file_name.clone()), - locale: (attributes.locale.as_str()).try_into().map_err( - |_| { - AttributeConversionError::Locale( - attributes.locale.clone(), - ) - }, - )?, - version: attributes.version, - volume: match &attributes.volume { - Some(v) => v.parse().ok(), - None => None, - }, - }) - } else { - None - } - }, - related: m.related.clone(), - }) - }) - .collect::, AttributeConversionError>>() - .map_err(ResponseConversionError::AttributeError)?, - }) - }) - .collect::, ResponseConversionError>>()?; + let mut data: Result, ResponseConversionError> = + Ok(Vec::with_capacity(search_response.data.len())); + for m in search_response.data { + if let Ok(ref mut d) = data { + match convert_data_to_manga(m) { + Ok(v) => d.push(v), + Err(e) => { + data = Err(e); + break; + } + } + } + } Ok(SearchResult { response, result, - data, + data: data?, }) } @@ -1080,20 +1024,18 @@ impl Display for ContentRating { } fn convert_attributes( - attributes: &ContentAttributes, + attributes: ContentAttributes, ) -> Result { Ok(MangaAttributes { - title: attributes.title.clone(), - alt_titles: attributes.alt_titles.clone(), - description: attributes.description.clone(), + title: attributes.title, + alt_titles: attributes.alt_titles, + description: attributes.description, is_locked: attributes.is_locked, - links: attributes.links.clone(), + links: attributes.links, original_language: (attributes.original_language.as_str()) .try_into() - .map_err(|_| { - AttributeConversionError::Language(attributes.original_language.clone()) - })?, - last_volume: match attributes.last_volume.clone() { + .map_err(|_| AttributeConversionError::Language(attributes.original_language))?, + last_volume: match attributes.last_volume { Some(s) => match s.parse() { Ok(v) => Some(v), Err(_) => { @@ -1106,7 +1048,7 @@ fn convert_attributes( }, None => None, }, - last_chapter: match attributes.last_chapter.clone() { + last_chapter: match attributes.last_chapter { Some(n) => match n.parse() { Ok(v) => Some(v), Err(_) => { @@ -1119,83 +1061,101 @@ fn convert_attributes( }, None => None, }, - publication_demographic: match attributes.publication_demographic.clone() { + publication_demographic: match attributes.publication_demographic { Some(s) => Some( (s.as_str()) .try_into() - .map_err(|_| AttributeConversionError::PublicationDemographic(s.clone()))?, + .map_err(|_| AttributeConversionError::PublicationDemographic(s))?, ), None => None, }, status: (attributes.status.as_str()) .try_into() - .map_err(|_| AttributeConversionError::Status(attributes.status.clone()))?, + .map_err(|_| AttributeConversionError::Status(attributes.status))?, year: attributes.year, - content_rating: attributes.content_rating.as_str().try_into().map_err(|_| { - AttributeConversionError::ContentRating(attributes.content_rating.clone()) - })?, - tags: attributes - .tags - .clone() - .iter() - .map(|m| { - Ok(Tag { - data_type: (m.type_name.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::DataType(m.type_name.clone()))?, - id: Id(m.id.clone()), - relationships: m - .relationships - .iter() - .map(|m| { - Ok(RelationShip { - id: Id(m.id.clone()), - data_type: (m.type_name.as_str()).try_into().map_err(|_| { - AttributeConversionError::DataType(m.type_name.clone()) - })?, - // TODO: Do this - attributes: None, - related: m.related.clone(), - }) + content_rating: attributes + .content_rating + .as_str() + .try_into() + .map_err(|_| AttributeConversionError::ContentRating(attributes.content_rating))?, + tags: { + let mut tags = Vec::with_capacity(attributes.tags.len()); + for m in attributes.tags { + tags.push(({ + || { + Ok::(Tag { + data_type: (m.type_name.as_str()) + .try_into() + .map_err(|_| AttributeConversionError::DataType(m.type_name))?, + id: Id(m.id), + relationships: { + let mut relationships = Vec::with_capacity(m.relationships.len()); + for m in m.relationships { + relationships.push(({ + || { + Ok::( + Relationship { + id: Id(m.id), + data_type: (m.type_name.as_str()) + .try_into() + .map_err(|_| { + AttributeConversionError::DataType( + m.type_name, + ) + })?, + // TODO: Do this + attributes: None, + related: m.related, + }, + ) + } + })()?); + } + relationships + }, + attributes: TagAttributes { + name: m.attributes.name, + group: m.attributes.group, + version: m.attributes.version, + description: Description { + en: m.attributes.description.en, + ru: m.attributes.description.ru, + }, + }, }) - .collect::, AttributeConversionError>>()?, - attributes: TagAttributes { - name: m.attributes.name.clone(), - group: m.attributes.group.clone(), - version: m.attributes.version, - description: Description { - en: m.attributes.description.en.clone(), - ru: m.attributes.description.ru.clone(), - }, - }, - }) - }) - .collect::, AttributeConversionError>>()?, + } + })()?); + } + tags + }, state: (attributes.state.as_str()) .try_into() - .map_err(|_| AttributeConversionError::State(attributes.state.clone()))?, + .map_err(|_| AttributeConversionError::State(attributes.state))?, chapter_numbers_reset_on_new_volume: attributes.chapter_numbers_reset_on_new_volume, created_at: DateTime::parse_from_rfc3339(&attributes.created_at).map_err(|_| { AttributeConversionError::CreatedAtDateTime(attributes.created_at.clone()) })?, - updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at).map_err(|_| { - AttributeConversionError::UpdatedAtDateTime(attributes.created_at.clone()) - })?, + updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) + .map_err(|_| AttributeConversionError::UpdatedAtDateTime(attributes.created_at))?, version: attributes.version, - available_translated_languages: attributes - .available_translated_languages - .iter() - .map(|m| { - Ok(match m { - Some(s) => Some( - (s.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::Language(s.clone()))?, - ), - None => None, - }) - }) - .collect::>, AttributeConversionError>>()?, + available_translated_languages: { + let mut av = Vec::with_capacity(attributes.available_translated_languages.len()); + for m in attributes.available_translated_languages { + av.push(({ + || { + Ok::, AttributeConversionError>(match m { + Some(s) => Some( + (s.as_str()) + .try_into() + .map_err(|_| AttributeConversionError::Language(s))?, + ), + None => None, + }) + } + })()?); + } + av + }, latest_uploaded_chapter: attributes .latest_uploaded_chapter .as_ref() @@ -1203,35 +1163,32 @@ fn convert_attributes( }) } -// ***** pub fn deserialize_id_query(json: &str) -> IdQueryResult { let id_query_response: IdQueryResponse = match serde_json::from_str(json) { Ok(v) => v, Err(e) => { - std::fs::write("out.json", json).unwrap(); eprintln!("ERROR: {:#?}", e); + std::fs::write("out.json", json).unwrap(); std::process::exit(1); } }; - convert_some_test(id_query_response).unwrap() + convert_id_query(id_query_response).unwrap() } -fn convert_some_test(input: IdQueryResponse) -> Result { +fn convert_id_query(response: IdQueryResponse) -> Result { Ok(IdQueryResult { - result: input.result.as_str().try_into().unwrap(), - response: input.response.as_str().try_into().unwrap(), - data: convert_data_to_manga(input.data).unwrap(), + result: response.result.as_str().try_into().unwrap(), + response: response.response.as_str().try_into().unwrap(), + data: convert_data_to_manga(response.data).unwrap(), }) } -// ***** - pub fn deserialize_chapter_feed(json: &str) -> ChapterFeed { let chapter_feed_response: ChapterFeedResponse = match serde_json::from_str(json) { Ok(v) => v, Err(e) => { - std::fs::write("out.json", json).unwrap(); eprintln!("ERROR: {:#?}", e); + std::fs::write("chapter_feed.json", json).unwrap(); std::process::exit(1); } }; @@ -1242,8 +1199,8 @@ pub fn deserializer(json: &str) -> SearchResult { let search_response: SearchResponse = match serde_json::from_str(json) { Ok(v) => v, Err(e) => { - std::fs::write("out.json", json).unwrap(); eprintln!("ERROR: {:#?}", e); + std::fs::write("search_result.json", json).unwrap(); std::process::exit(1); } }; @@ -1268,12 +1225,12 @@ enum ChapterFeedConversionError { enum ChapterConversionError { DataType(String), Id(String), - RelationShip(ChapterRelationShipError), + Relationship(ChapterRelationshipError), Attributes(ChapterAttributeConversionError), } #[derive(Debug)] -enum ChapterRelationShipError { +enum ChapterRelationshipError { TypeData(String), Id(String), } @@ -1281,41 +1238,59 @@ enum ChapterRelationShipError { fn convert_chapter_feed( feed: ChapterFeedResponse, ) -> Result { + // Now this is a bit of an abomination. It uses closures such that you can use the ? syntax + // sugar to return an error. Also uses for loops instead of iterators since they do not take + // ownership. I think I should have just kept the iterators. + let mut data: Vec = Vec::with_capacity(feed.data.len()); + for m in feed.data { + let chapter: Chapter = ({ + || { + Ok::(Chapter { + data_type: (m.type_name.as_str()) + .try_into() + .map_err(|_| ChapterConversionError::DataType(m.type_name))?, + id: Id(m.id), + attributes: convert_chapter_attributes(m.attributes) + .map_err(ChapterConversionError::Attributes)?, + relationships: { + let mut relationships = Vec::with_capacity(m.relationships.len()); + for r in m.relationships { + relationships.push( + ({ + || { + Ok({ + ChapterRelationship { + data_type: (r.type_name.as_str()) + .try_into() + .map_err(|_| { + ChapterRelationshipError::TypeData( + r.type_name, + ) + })?, + id: Id(r.id), + } + }) + } + })() + .map_err(ChapterConversionError::Relationship)?, + ); + } + relationships + }, + }) + } + })() + .map_err(ChapterFeedConversionError::Chapter)?; + data.push(chapter); + } Ok(ChapterFeed { result: (feed.result.as_str()) .try_into() - .map_err(|_| ChapterFeedConversionError::Result(feed.result.clone()))?, + .map_err(|_| ChapterFeedConversionError::Result(feed.result))?, response: (feed.response.as_str()) .try_into() - .map_err(|_| ChapterFeedConversionError::Result(feed.response.clone()))?, - data: feed - .data - .iter() - .map(|m| { - Ok(Chapter { - data_type: (m.type_name.as_str()) - .try_into() - .map_err(|_| ChapterConversionError::DataType(m.type_name.clone()))?, - id: Id(m.id.clone()), - attributes: convert_chapter_attributes(&m.attributes) - .map_err(ChapterConversionError::Attributes)?, - relationships: m - .relationships - .iter() - .map(|m| { - Ok(ChapterRelationShip { - data_type: (m.type_name.as_str()).try_into().map_err(|_| { - ChapterRelationShipError::TypeData(m.type_name.clone()) - })?, - id: Id(m.id.clone()), - }) - }) - .collect::, ChapterRelationShipError>>() - .map_err(ChapterConversionError::RelationShip)?, - }) - }) - .collect::, ChapterConversionError>>() - .map_err(ChapterFeedConversionError::Chapter)?, + .map_err(|_| ChapterFeedConversionError::Result(feed.response))?, + data, limit: feed.limit, offset: feed.offset, total: feed.total, @@ -1333,7 +1308,7 @@ enum ChapterAttributeConversionError { } fn convert_chapter_attributes( - attributes: &ChapterAttributesContent, + attributes: ChapterAttributesContent, ) -> Result { Ok(ChapterAttributes { volume: match &attributes.volume { @@ -1350,24 +1325,19 @@ fn convert_chapter_attributes( }, None => None, }, - created_at: DateTime::parse_from_rfc3339(&attributes.created_at).map_err(|_| { - ChapterAttributeConversionError::CreatedAt(attributes.created_at.clone()) - })?, - published_at: DateTime::parse_from_rfc3339(&attributes.publish_at).map_err(|_| { - ChapterAttributeConversionError::CreatedAt(attributes.publish_at.clone()) - })?, - updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at).map_err(|_| { - ChapterAttributeConversionError::CreatedAt(attributes.updated_at.clone()) - })?, - external_url: attributes.external_url.clone(), - title: attributes.title.clone(), + created_at: DateTime::parse_from_rfc3339(&attributes.created_at) + .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.created_at))?, + published_at: DateTime::parse_from_rfc3339(&attributes.publish_at) + .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.publish_at))?, + updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) + .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.updated_at))?, + external_url: attributes.external_url, + title: attributes.title, pages: attributes.pages, translated_language: (attributes.translated_language.as_str()) .try_into() .map_err(|_| { - ChapterAttributeConversionError::TranslatedLanguage( - attributes.translated_language.clone(), - ) + ChapterAttributeConversionError::TranslatedLanguage(attributes.translated_language) })?, version: attributes.version, }) @@ -1377,7 +1347,7 @@ fn convert_chapter_images(data: ChapterImagesContent) -> Result Result Result { Ok(Manga { - id: Id(m.id.clone()), + id: Id(m.id), data_type: (m.type_name.as_str()).try_into().map_err(|_| { - ResponseConversionError::AttributeError(AttributeConversionError::DataType( - m.type_name.clone(), - )) + ResponseConversionError::AttributeError(AttributeConversionError::DataType(m.type_name)) })?, - attributes: convert_attributes(&m.attributes) + attributes: convert_attributes(m.attributes) .map_err(ResponseConversionError::AttributeError)?, - relationships: m - .relationships - .iter() - .map(|m| { - Ok(RelationShip { - id: Id(m.id.clone()), - data_type: (m.type_name.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::DataType(m.type_name.clone()))?, - attributes: { - if let Some(attributes) = &m.attributes { - Some(CoverAttributes { - created_at: DateTime::parse_from_rfc3339(&attributes.created_at) - .map_err(|_| { - AttributeConversionError::CreatedAtDateTime( - attributes.created_at.clone(), - ) - })?, - updated_at: DateTime::parse_from_rfc3339(&attributes.created_at) - .map_err(|_| { - AttributeConversionError::CreatedAtDateTime( - attributes.created_at.clone(), - ) - })?, - // TODO: Something should probably be done here - description: String::new(), - file_name: Id(attributes.file_name.clone()), - locale: (attributes.locale.as_str()).try_into().map_err(|_| { - AttributeConversionError::Locale(attributes.locale.clone()) - })?, - version: attributes.version, - volume: match &attributes.volume { - Some(v) => v.parse().ok(), - None => None, + relationships: { + let mut relationships = Vec::with_capacity(m.relationships.len()); + for m in m.relationships { + relationships.push( + ({ + || { + Ok::(Relationship { + id: Id(m.id), + data_type: m + .type_name + .as_str() + .try_into() + .map_err(|_| AttributeConversionError::DataType(m.type_name))?, + attributes: { + if let Some(attributes) = m.attributes { + Some(CoverAttributes { + created_at: DateTime::parse_from_rfc3339( + &attributes.created_at, + ) + .map_err(|_| { + AttributeConversionError::CreatedAtDateTime( + attributes.created_at.clone(), + ) + })?, + updated_at: DateTime::parse_from_rfc3339( + &attributes.created_at, + ) + .map_err(|_| { + AttributeConversionError::CreatedAtDateTime( + attributes.created_at.clone(), + ) + })?, + // TODO: Something should probably be done here + description: String::new(), + file_name: Id(attributes.file_name.clone()), + locale: (attributes.locale.as_str()) + .try_into() + .map_err(|_| { + AttributeConversionError::Locale( + attributes.locale.clone(), + ) + })?, + version: attributes.version, + volume: match &attributes.volume { + Some(v) => v.parse().ok(), + None => None, + }, + }) + } else { + None + } }, + related: m.related.clone(), }) - } else { - None } - }, - related: m.related.clone(), - }) - }) - .collect::, AttributeConversionError>>() - .map_err(ResponseConversionError::AttributeError)?, + })() + .map_err(ResponseConversionError::AttributeError)?, + ); + } + relationships + }, }) } diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..224e035 --- /dev/null +++ b/src/test.rs @@ -0,0 +1,14 @@ +extern crate test; + +#[cfg(test)] +mod tests { + use super::*; + use crate::response_deserializer; + use test::Bencher; + + #[bench] + fn loops(b: &mut Bencher) { + let search_result = std::fs::read_to_string("test_data/search_result.json").unwrap(); + b.iter(|| response_deserializer::deserializer(&search_result)); + } +} diff --git a/src/util.rs b/src/util.rs index 69836a5..275c254 100644 --- a/src/util.rs +++ b/src/util.rs @@ -51,6 +51,7 @@ pub struct Config { pub selection_type: Option, pub selection_range: Option, pub search: Option, + pub cover: Option, } pub enum ConfigSearch { @@ -308,6 +309,7 @@ impl Config { result_limit: 5, selection_type: None, selection_range: None, + cover: None, } } } @@ -429,6 +431,22 @@ pub fn args() -> Config { } }; } + "--cover" => { + config.cover = match args.next() { + Some(s) => Some(match s.as_str() { + "true" => true, + "false" => false, + s => { + eprintln!("Invalid value for cover size, valid values: [\"256\", \"512\", \"full\"], found: {s}"); + std::process::exit(1); + } + }), + None => { + eprintln!("Missing value for cover size, valid values: [\"256\", \"512\", \"full\"]"); + std::process::exit(1); + } + }; + } s => { eprintln!("Found invalid argument: {s}"); std::process::exit(1);