diff --git a/palette/Cargo.toml b/palette/Cargo.toml index bccd17edc..49d90adb0 100644 --- a/palette/Cargo.toml +++ b/palette/Cargo.toml @@ -78,15 +78,15 @@ serde_json = "1" enterpolation = "0.2.0" [dev-dependencies.clap] -version = "2" +version = "3.2.23" default-features = false [dev-dependencies.criterion] -version = "0.3" +version = "0.4.0" default-features = false [dev-dependencies.image] -version = "0.23" +version = "0.23.14" default-features = false features = ["png"] diff --git a/palette/examples/color_scheme.rs b/palette/examples/color_scheme.rs index b850a1e66..0420334b0 100644 --- a/palette/examples/color_scheme.rs +++ b/palette/examples/color_scheme.rs @@ -2,126 +2,110 @@ use palette::{Darken, IntoColor, Lch, Lighten, LinSrgb, ShiftHue, Srgb}; use image::{GenericImage, GenericImageView, RgbImage, SubImage}; -use clap::{App, AppSettings, Arg, SubCommand}; +use clap::{Arg, Command}; const SWATCH_SIZE: u32 = 128; fn main() { - let matches = App::new("color_scheme") + let matches = Command::new("color_scheme") .about("Generates a very simple color scheme from an RGB8 color.") - .setting(AppSettings::ArgRequiredElseHelp) + .arg_required_else_help(true) .arg( - Arg::with_name("red") + Arg::new("red") .required(true) - .empty_values(false) + .value_parser(clap::value_parser!(u8)) .index(1) .help("[0-255] The red channel of the primary color."), ) .arg( - Arg::with_name("green") + Arg::new("green") .required(true) - .empty_values(false) + .value_parser(clap::value_parser!(u8)) .index(2) .help("[0-255] The green channel of the primary color."), ) .arg( - Arg::with_name("blue") + Arg::new("blue") .required(true) - .empty_values(false) + .value_parser(clap::value_parser!(u8)) .index(3) .help("[0-255] The blue channel of the primary color."), ) .subcommand( - SubCommand::with_name("triad") + Command::new("triad") .about("A three point scheme, centered around the complementary.") .arg( - Arg::with_name("distance") + Arg::new("distance") .help("The distance between the secondary colors.") .long("distance") - .short("d") + .short('d') .value_name("degrees") - .takes_value(true) - .empty_values(false), + .value_parser(clap::value_parser!(f32)), ), ) .subcommand( - SubCommand::with_name("analogous") + Command::new("analogous") .about("Like triad, but centered around the primary.") .arg( - Arg::with_name("distance") + Arg::new("distance") .help("The distance between the secondary colors.") .long("distance") - .short("d") + .short('d') .value_name("degrees") - .takes_value(true) - .empty_values(false), + .value_parser(clap::value_parser!(f32)), ), ) .subcommand( - SubCommand::with_name("rectangle") - .about("A four point scheme.") - .arg( - Arg::with_name("distance") - .help("The distance to the closest secondary colors.") - .long("distance") - .short("d") - .value_name("degrees") - .takes_value(true) - .empty_values(false), - ), - ) - .subcommand( - SubCommand::with_name("complementary").about("A simple two point color scheme."), + Command::new("rectangle").about("A four point scheme.").arg( + Arg::new("distance") + .help("The distance to the closest secondary colors.") + .long("distance") + .short('d') + .value_name("degrees") + .value_parser(clap::value_parser!(f32)), + ), ) + .subcommand(Command::new("complementary").about("A simple two point color scheme.")) .get_matches(); //Get the components of the primary color let red: u8 = matches - .value_of("red") - .and_then(|r| r.parse().ok()) + .get_one::("red") + .copied() .expect("the red channel must be a number in the range [0-255]"); let green: u8 = matches - .value_of("green") - .and_then(|r| r.parse().ok()) + .get_one::("green") + .copied() .expect("the green channel must be a number in the range [0-255]"); let blue: u8 = matches - .value_of("blue") - .and_then(|r| r.parse().ok()) + .get_one::("blue") + .copied() .expect("the blue channel must be a number in the range [0-255]"); let primary: Lch = Srgb::new(red, green, blue).into_linear().into_color(); //Generate the secondary colors, depending on the input arguments let secondary = match matches.subcommand() { - ("triad", matches) | ("", matches) => { + Some(("triad", matches)) | Some(("", matches)) => { //Two secondary colors that are close to the complementary, or evenly spaced - let distance: f32 = matches - .and_then(|m| m.value_of("distance")) - .and_then(|d| d.parse().ok()) - .unwrap_or(120.0); + let distance = matches.get_one::("distance").copied().unwrap_or(120.0); let shift = 180.0 - (distance / 2.0); vec![primary.shift_hue(shift), primary.shift_hue(-shift)] } - ("analogous", matches) => { + Some(("analogous", matches)) => { //Two secondary colors that are close to the primary - let distance: f32 = matches - .and_then(|m| m.value_of("distance")) - .and_then(|d| d.parse().ok()) - .unwrap_or(60.0); + let distance = matches.get_one::("distance").copied().unwrap_or(60.0); let shift = distance / 2.0; vec![primary.shift_hue(shift), primary.shift_hue(-shift)] } - ("rectangle", matches) => { + Some(("rectangle", matches)) => { //Three secondary colors that forms a rectangle or a square, together with the // primary - let distance: f32 = matches - .and_then(|m| m.value_of("distance")) - .and_then(|d| d.parse().ok()) - .unwrap_or(90.0); + let distance = matches.get_one::("distance").copied().unwrap_or(90.0); let shift1 = distance; let shift2 = 180.0 + distance; @@ -132,8 +116,9 @@ fn main() { primary.shift_hue(shift2), ] } - ("complementary", _) => vec![primary.shift_hue(180.0)], // Simply the complementary color - (name, _) => panic!("unknown subcommand: {}", name), + Some(("complementary", _)) => vec![primary.shift_hue(180.0)], // Simply the complementary color + Some((name, _)) => panic!("unknown subcommand: {}", name), + None => panic!("expected a subcommand"), }; //Create an image for the swatches diff --git a/palette_derive/Cargo.toml b/palette_derive/Cargo.toml index 32497ca1e..5a6a7a1fa 100644 --- a/palette_derive/Cargo.toml +++ b/palette_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "palette_derive" -version = "0.6.1" #automatically updated +version = "0.6.1" #automatically updated authors = ["Erik Hedvall "] exclude = [] description = "Automatically implement traits from the palette crate." @@ -17,7 +17,7 @@ proc-macro = true bench = false [dependencies] -syn = { version = "^1.0", default-features = false, features = [ +syn = { version = "2.0.13", default-features = false, features = [ "derive", "parsing", "printing", diff --git a/palette_derive/src/cast/array_cast.rs b/palette_derive/src/cast/array_cast.rs index 340f9ec53..d3c45f312 100644 --- a/palette_derive/src/cast/array_cast.rs +++ b/palette_derive/src/cast/array_cast.rs @@ -2,7 +2,9 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::{quote, ToTokens}; -use syn::{Attribute, Data, DeriveInput, Fields, Ident, Type}; +use syn::{ + punctuated::Punctuated, token::Comma, Attribute, Data, DeriveInput, Fields, Meta, Path, Type, +}; use crate::meta::{self, FieldAttributes, IdentOrIndex, TypeItemAttributes}; use crate::util; @@ -125,10 +127,19 @@ fn is_allowed_repr(attributes: &[Attribute]) -> std::result::Result list, + Err(error) => { + errors.push(error); + continue; + } + }; + + let items = match meta_list.parse_args_with(Punctuated::::parse_terminated) + { Ok(items) => items, Err(error) => { errors.push(error); @@ -136,9 +147,12 @@ fn is_allowed_repr(attributes: &[Attribute]) -> std::result::Result { let substitute = if let Meta::NameValue(MetaNameValue { - lit: Lit::Str(string), + value: + Expr::Lit(ExprLit { + lit: Lit::Str(string), + .. + }), .. }) = argument { diff --git a/palette_derive/src/meta/mod.rs b/palette_derive/src/meta/mod.rs index 28f52bf74..0d05dea0b 100644 --- a/palette_derive/src/meta/mod.rs +++ b/palette_derive/src/meta/mod.rs @@ -1,10 +1,11 @@ use proc_macro2::TokenStream; -use syn::parse::{Parse, ParseBuffer, ParseStream, Parser, Result}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - parenthesized, Attribute, Fields, Ident, Index, Lit, LitStr, Meta, NestedMeta, Token, Type, + parse::{Parse, ParseStream, Parser, Result}, + token::Comma, }; +use syn::{Attribute, Fields, Ident, Index, LitStr, Meta, Token, Type}; pub use self::field_attributes::*; pub use self::type_item_attributes::*; @@ -20,7 +21,8 @@ pub fn parse_namespaced_attributes( for attribute in attributes { let is_palette_attribute = attribute - .path + .meta + .path() .get_ident() .map(|name| name == "palette") .unwrap_or(false); @@ -29,25 +31,29 @@ pub fn parse_namespaced_attributes( continue; } - if attribute.tokens.is_empty() { + let meta_list = match attribute.meta.require_list() { + Ok(list) => list, + Err(error) => { + errors.push(error); + continue; + } + }; + + if meta_list.tokens.is_empty() { errors.push(::syn::parse::Error::new( - attribute.path.span(), + attribute.path().span(), "expected `palette(...)`", )); continue; } - let parse_result = parse_meta_list.parse2(attribute.tokens); + let parse_result = + Punctuated::<_, Comma>::parse_terminated.parse2(meta_list.tokens.clone()); match parse_result { Ok(meta) => { for argument in meta { - let argument_result = match argument { - NestedMeta::Meta(argument) => result.argument(argument), - NestedMeta::Lit(literal) => result.literal(literal), - }; - - if let Err(error) = argument_result { + if let Err(error) = result.argument(argument) { errors.push(error); } } @@ -84,7 +90,7 @@ pub fn parse_field_attributes( for (field_name, ty, attribute) in attributes { let is_palette_attribute = attribute - .path + .path() .get_ident() .map(|name| name == "palette") .unwrap_or(false); @@ -93,25 +99,29 @@ pub fn parse_field_attributes( continue; } - if attribute.tokens.is_empty() { + let meta_list = match attribute.meta.require_list() { + Ok(list) => list, + Err(error) => { + errors.push(error); + continue; + } + }; + + if meta_list.tokens.is_empty() { errors.push(::syn::parse::Error::new( - attribute.path.span(), + attribute.path().span(), "expected `palette(...)`", )); continue; } - let parse_result = parse_meta_list.parse2(attribute.tokens); + let parse_result = + Punctuated::<_, Comma>::parse_terminated.parse2(meta_list.tokens.clone()); match parse_result { Ok(meta) => { for argument in meta { - let argument_result = match argument { - NestedMeta::Meta(argument) => result.argument(&field_name, &ty, argument), - NestedMeta::Lit(literal) => result.literal(&field_name, &ty, literal), - }; - - if let Err(error) = argument_result { + if let Err(error) = result.argument(&field_name, &ty, argument) { errors.push(error); } } @@ -138,34 +148,6 @@ pub fn assert_path_meta(meta: &Meta) -> Result<()> { Ok(()) } -pub fn parse_tuple_attribute(tts: TokenStream) -> Result> { - fn parse_generic_tuple(input: ParseStream) -> Result> { - let content; - parenthesized!(content in input); - - let mut tuple = Vec::new(); - loop { - tuple.push(content.parse()?); - if content.is_empty() { - break; - } - content.parse::()?; - if content.is_empty() { - break; - } - } - Ok(tuple) - } - - parse_generic_tuple.parse2(tts) -} - -fn parse_meta_list(buffer: &ParseBuffer) -> syn::Result> { - let inner; - parenthesized!(inner in buffer); - syn::punctuated::Punctuated::parse_terminated(&inner) -} - #[derive(PartialEq)] pub struct KeyValuePair { pub key: Ident, @@ -244,24 +226,8 @@ pub trait DataMetaParser: Default { pub trait AttributeArgumentParser: Default { fn argument(&mut self, argument: Meta) -> Result<()>; - - fn literal(&mut self, literal: Lit) -> Result<()> { - Err(::syn::parse::Error::new( - literal.span(), - "unexpected literal", - )) - } } pub trait FieldAttributeArgumentParser: Default { fn argument(&mut self, field_name: &IdentOrIndex, ty: &Type, argument: Meta) -> Result<()>; - - fn literal(&mut self, field_name: &IdentOrIndex, ty: &Type, literal: Lit) -> Result<()> { - let (_, _) = (field_name, ty); - - Err(::syn::parse::Error::new( - literal.span(), - "unexpected literal", - )) - } } diff --git a/palette_derive/src/meta/type_item_attributes.rs b/palette_derive/src/meta/type_item_attributes.rs index 2283f7a77..5a5137347 100644 --- a/palette_derive/src/meta/type_item_attributes.rs +++ b/palette_derive/src/meta/type_item_attributes.rs @@ -1,8 +1,8 @@ use std::collections::HashSet; use quote::quote; -use syn::spanned::Spanned; -use syn::{Ident, Lit, Meta, MetaNameValue, NestedMeta, Result, Type}; +use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Expr, ExprLit}; +use syn::{Ident, Lit, Meta, MetaNameValue, Result, Type}; use super::AttributeArgumentParser; @@ -23,33 +23,15 @@ impl AttributeArgumentParser for TypeItemAttributes { match argument_name.as_deref() { Some("skip_derives") => { - let result = if let Meta::List(list) = argument { - let skipped: ::std::result::Result<_, _> = list - .nested - .into_iter() - .map(|value| match value { - NestedMeta::Meta(Meta::Path(path)) => { - if let Some(name) = path.get_ident() { - Ok(name.clone()) - } else { - Err(path.span()) - } - } - value => Err(value.span()), - }) - .collect(); + if let Meta::List(list) = argument { + let skipped = + list.parse_args_with(Punctuated::::parse_terminated)?; - skipped.map(|values: Vec| { - self.skip_derives - .extend(values.into_iter().map(|ident| ident.to_string())); - }) + self.skip_derives + .extend(skipped.into_iter().map(|ident| ident.to_string())); } else { - Err(argument.span()) - }; - - if let Err(span) = result { return Err(::syn::parse::Error::new( - span, + argument.span(), "expected `skip_derives` to have a list of color type names, like `skip_derives(Xyz, Luma, Rgb)`", )); } @@ -57,7 +39,11 @@ impl AttributeArgumentParser for TypeItemAttributes { Some("component") => { if self.component.is_none() { let result = if let Meta::NameValue(MetaNameValue { - lit: Lit::Str(ty), .. + value: + Expr::Lit(ExprLit { + lit: Lit::Str(ty), .. + }), + .. }) = argument { self.component = Some(ty.parse()?); @@ -80,7 +66,11 @@ impl AttributeArgumentParser for TypeItemAttributes { Some("white_point") => { if self.white_point.is_none() { let result = if let Meta::NameValue(MetaNameValue { - lit: Lit::Str(ty), .. + value: + Expr::Lit(ExprLit { + lit: Lit::Str(ty), .. + }), + .. }) = argument { self.white_point = Some(ty.parse()?); @@ -103,7 +93,11 @@ impl AttributeArgumentParser for TypeItemAttributes { Some("rgb_standard") => { if self.rgb_standard.is_none() { let result = if let Meta::NameValue(MetaNameValue { - lit: Lit::Str(ty), .. + value: + Expr::Lit(ExprLit { + lit: Lit::Str(ty), .. + }), + .. }) = argument { self.rgb_standard = Some(ty.parse()?); @@ -126,7 +120,11 @@ impl AttributeArgumentParser for TypeItemAttributes { Some("luma_standard") => { if self.luma_standard.is_none() { let result = if let Meta::NameValue(MetaNameValue { - lit: Lit::Str(ty), .. + value: + Expr::Lit(ExprLit { + lit: Lit::Str(ty), .. + }), + .. }) = argument { self.luma_standard = Some(ty.parse()?);