// Copyright 2020 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! This mod provides common constants, structs and functions used across cros_alsa_derive. use std::convert::TryFrom; use syn::{Lit, Meta, NestedMeta, Result}; /// Attribute name of cros_alsa. const CROS_ALSA: &str = "cros_alsa"; /// const for `#[cros_alsa(path = "...")]` const PATH: &str = "path"; /// Possible `#[cros_alsa("...")]` derive macro helper attributes. pub enum CrosAlsaAttr { /// Use `#[cros_alsa(path = "crate")]` to replace the crate path of cros_alsa in derive macros. Path(syn::Path), } impl CrosAlsaAttr { /// Return true if the value is a `Path` variant. pub fn is_path(&self) -> bool { use CrosAlsaAttr::*; match self { Path(_) => true, // suppress unreachable_patterns warning because there is only one CrosAlsaAttr variant. #[allow(unreachable_patterns)] _ => false, } } } impl TryFrom for CrosAlsaAttr { type Error = syn::Error; fn try_from(meta_item: NestedMeta) -> Result { match meta_item { // Parse `#[cros_alsa(path = "crate")]` NestedMeta::Meta(Meta::NameValue(m)) if m.path.is_ident(PATH) => { if let Lit::Str(lit_str) = &m.lit { return Ok(CrosAlsaAttr::Path(lit_str.parse()?)); } Err(syn::Error::new_spanned( &m.lit, "expected a valid path for cros_alsa_derive::CrosAlsaAttr::Path", )) } _ => Err(syn::Error::new_spanned( meta_item, "unrecognized cros_alsa_derive::CrosAlsaAttr", )), } } } /// Parses `#[cros_alsa(path = "...")]` into a list of `CrosAlsaAttr`. /// It's used to replace the crate path of cros_alsa in derive macros. pub fn parse_cros_alsa_attr(attrs: &[syn::Attribute]) -> Result> { attrs .iter() .flat_map(|attr| get_cros_alsa_meta_items(attr)) .flatten() .map(CrosAlsaAttr::try_from) .collect() } /// Parses and collects `NestedMeta` under `cros_alsa` attribute. fn get_cros_alsa_meta_items(attr: &syn::Attribute) -> Result> { if !attr.path.is_ident(CROS_ALSA) { return Ok(Vec::new()); } match attr.parse_meta() { Ok(Meta::List(meta)) => Ok(meta.nested.into_iter().collect()), _ => Err(syn::Error::new_spanned(attr, "expected #[cros_alsa(...)]")), } }