blob: 2daaa3be5aea587fe963027c94bd5e10f6df3347 [file] [log] [blame]
// 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<syn::NestedMeta> for CrosAlsaAttr {
type Error = syn::Error;
fn try_from(meta_item: NestedMeta) -> Result<CrosAlsaAttr> {
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<Vec<CrosAlsaAttr>> {
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<Vec<syn::NestedMeta>> {
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(...)]")),
}
}