diff --git a/Cargo.lock b/Cargo.lock index f98dde8..70ae4d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1527,7 +1527,7 @@ checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" [[package]] name = "site_icons" -version = "0.1.4" +version = "0.1.5" dependencies = [ "byteorder", "clap", @@ -1549,6 +1549,7 @@ dependencies = [ "tokio 1.1.1", "tokio-futures-byteorder", "url", + "vec1", ] [[package]] @@ -1990,6 +1991,15 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" +[[package]] +name = "vec1" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fffd117bafd8d50c625cce64efde70f416ce6d38f78104035a0913cf15fe97" +dependencies = [ + "serde", +] + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index 5c86c02..b13446a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "site_icons" -version = "0.1.4" +version = "0.1.5" authors = ["Sam Denty "] edition = "2018" license = "GPL-3.0" @@ -19,6 +19,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] clap = "3.0.0-beta.2" +vec1 = { version = "1.6.0", features = ["serde"] } itertools = "0.10.0" serde_with = "1.6.2" html5ever = "0.25.1" diff --git a/src/bin/site-icons.rs b/src/bin/site-icons.rs index 7a200e5..8350069 100644 --- a/src/bin/site-icons.rs +++ b/src/bin/site-icons.rs @@ -21,7 +21,7 @@ async fn main() -> Result<(), Box> { if opts.debug { let mut builder = Builder::new(); - builder.filter_module("info", LevelFilter::Info); + builder.filter_level(LevelFilter::Info); builder.init(); } diff --git a/src/icon_info.rs b/src/icon_info.rs index d2d5508..e4946a8 100644 --- a/src/icon_info.rs +++ b/src/icon_info.rs @@ -19,7 +19,7 @@ enum IconType { ICO, } -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(tag = "type")] #[serde(rename_all = "lowercase")] pub enum IconInfo { @@ -76,15 +76,19 @@ impl IconInfo { let kind = match (mime.type_(), mime.subtype()) { (mime::IMAGE, mime::PNG) => { - if let Some(size) = sizes.map(|s| s.into_largest()) { - return Ok(IconInfo::PNG { size }); + if let Some(sizes) = sizes { + return Ok(IconInfo::PNG { + size: *sizes.largest(), + }); } IconType::PNG } (mime::IMAGE, mime::JPEG) => { - if let Some(size) = sizes.map(|s| s.into_largest()) { - return Ok(IconInfo::JPEG { size }); + if let Some(sizes) = sizes { + return Ok(IconInfo::JPEG { + size: *sizes.largest(), + }); } IconType::JPEG } @@ -125,6 +129,14 @@ impl IconInfo { IconInfo::SVG => None, } } + + pub fn sizes(&self) -> Option { + match self { + IconInfo::ICO { sizes } => Some((*sizes).clone()), + IconInfo::PNG { size } | IconInfo::JPEG { size } => Some((*size).into()), + IconInfo::SVG => None, + } + } } impl Display for IconInfo { diff --git a/src/icon_size/ico.rs b/src/icon_size/ico.rs index 81aff95..3c7d64f 100644 --- a/src/icon_size/ico.rs +++ b/src/icon_size/ico.rs @@ -1,7 +1,8 @@ -use super::{png::get_png_sizes, IconSizes}; +use super::{png::get_png_sizes, IconSize, IconSizes}; use byteorder::{LittleEndian, ReadBytesExt}; use futures::prelude::*; use std::{ + convert::TryInto, error::Error, io::{Cursor, Seek, SeekFrom}, }; @@ -32,7 +33,7 @@ pub async fn get_ico_sizes( offset += data.len(); let mut data = Cursor::new(data); - let mut sizes = IconSizes::new(); + let mut sizes = Vec::new(); for i in 0..icon_count { data.seek(SeekFrom::Start((INDEX_SIZE * i) as _))?; @@ -52,11 +53,9 @@ pub async fn get_ico_sizes( sizes.push(size); } } else { - sizes.add_size(width as _, height as _) + sizes.push(IconSize::new(width as _, height as _)) } } - sizes.sort(); - - Ok(sizes) + Ok(sizes.try_into()?) } diff --git a/src/icon_size/icon_sizes.rs b/src/icon_size/icon_sizes.rs index 514755b..ec3af56 100644 --- a/src/icon_size/icon_sizes.rs +++ b/src/icon_size/icon_sizes.rs @@ -4,13 +4,16 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::{ cmp::Ordering, + convert::{TryFrom, TryInto}, error::Error, fmt::{self, Display}, - ops::{Deref, DerefMut}, + ops::Deref, }; +use vec1::{vec1, Vec1}; -#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] -pub struct IconSizes(Vec); +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +/// Icon sizes, ordered from largest to smallest +pub struct IconSizes(Vec1); impl Display for IconSizes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -19,52 +22,44 @@ impl Display for IconSizes { } impl IconSizes { - pub fn new() -> Self { - IconSizes(Vec::new()) - } - pub fn from_str(sizes_str: &str) -> Result> { let size_strs = sizes_str.split(" "); - let mut sizes = IconSizes::new(); + let mut sizes = Vec::new(); for size in size_strs { if let Ok(size) = serde_json::from_value(Value::String(size.to_string())) { sizes.push(size); } } - if sizes.is_empty() { - return Err("must contain a size".into()); - } - - sizes.sort(); - - Ok(sizes) + Ok(sizes.try_into()?) } - pub fn add_size(&mut self, width: u32, height: u32) { - self.push(IconSize::new(width, height)) + pub fn add_size(&mut self, size: IconSize) { + match self.0.binary_search(&size) { + Ok(_) => {} + Err(pos) => self.0.insert(pos, size), + } } pub fn largest(&self) -> &IconSize { - &self.0[0] - } - - pub fn into_largest(self) -> IconSize { - self.0.into_iter().next().unwrap() + self.0.first() } } impl Deref for IconSizes { - type Target = Vec; - fn deref(&self) -> &Vec { + type Target = Vec1; + fn deref(&self) -> &Vec1 { &self.0 } } -impl DerefMut for IconSizes { - fn deref_mut(&mut self) -> &mut Vec { - &mut self.0 +impl IntoIterator for IconSizes { + type Item = IconSize; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() } } @@ -79,3 +74,21 @@ impl PartialOrd for IconSizes { Some(self.cmp(other)) } } + +impl TryFrom> for IconSizes { + type Error = String; + + fn try_from(mut vec: Vec) -> Result { + vec.sort(); + + Ok(IconSizes( + vec.try_into().map_err(|_| "must contain a size")?, + )) + } +} + +impl From for IconSizes { + fn from(size: IconSize) -> Self { + IconSizes(vec1![size]) + } +} diff --git a/src/icon_size/mod.rs b/src/icon_size/mod.rs index 257ba91..9de9f6a 100644 --- a/src/icon_size/mod.rs +++ b/src/icon_size/mod.rs @@ -10,14 +10,14 @@ pub use png::*; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::{ - cmp::Ordering, + cmp::{self, Ordering}, error::Error, fmt::{self, Display}, io::{Read, Seek, SeekFrom}, }; #[serde_as] -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct IconSize { pub width: u32, pub height: u32, @@ -33,13 +33,15 @@ impl IconSize { pub fn new(width: u32, height: u32) -> Self { Self { width, height } } + + pub fn max_size(&self) -> u32 { + cmp::max(self.width, self.height) + } } impl Ord for IconSize { fn cmp(&self, other: &Self) -> Ordering { - let self_res = self.width * self.height; - let other_res = other.width * other.height; - other_res.cmp(&self_res) + other.max_size().cmp(&self.max_size()) } }