macros: sanitize/validate metric name (#95)

* macros: sanitize metric name

* bump msrv for tests
This commit is contained in:
Toby Lawrence 2020-09-27 18:00:16 -04:00 committed by GitHub
parent 72602ce6fb
commit ebcaab37c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 59 additions and 9 deletions

View File

@ -24,7 +24,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
rust_version: ['1.42.0', 'stable', 'nightly']
rust_version: ['1.43.0', 'stable', 'nightly']
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v1

View File

@ -23,3 +23,5 @@ syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
proc-macro-hack = "0.5"
lazy_static = "1.4"
regex = "1.3"

View File

@ -2,8 +2,11 @@ extern crate proc_macro;
use self::proc_macro::TokenStream;
use lazy_static::lazy_static;
use proc_macro2::Span;
use proc_macro_hack::proc_macro_hack;
use quote::{format_ident, quote, ToTokens};
use regex::Regex;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{parse_macro_input, Expr, LitStr, Token};
@ -15,6 +18,15 @@ enum Key {
Scoped(LitStr),
}
impl Key {
pub fn span(&self) -> Span {
match self {
Key::Scoped(s) => s.span(),
Key::NotScoped(s) => s.span(),
}
}
}
enum Labels {
Existing(Expr),
Inline(Vec<(LitStr, Expr)>),
@ -262,14 +274,31 @@ fn can_use_fast_path(labels: &Option<Labels>) -> bool {
}
fn read_key(input: &mut ParseStream) -> Result<Key> {
if let Ok(_) = input.parse::<Token![<]>() {
let key = if let Ok(_) = input.parse::<Token![<]>() {
let s = input.parse::<LitStr>()?;
input.parse::<Token![>]>()?;
Ok(Key::Scoped(s))
Key::Scoped(s)
} else {
let s = input.parse::<LitStr>()?;
Ok(Key::NotScoped(s))
Key::NotScoped(s)
};
let inner = match key {
Key::Scoped(ref s) => s.value(),
Key::NotScoped(ref s) => s.value(),
};
lazy_static! {
static ref RE: Regex = Regex::new("^[a-zA-Z_:\\.][a-zA-Z0-9_:\\.]*$").unwrap();
}
if !RE.is_match(&inner) {
return Err(Error::new(
key.span(),
"metric name must match ^[a-zA-Z_:.][a-zA-Z0-9_:.]*$",
));
}
Ok(key)
}
fn quote_key_name(key: Key) -> proc_macro2::TokenStream {

6
metrics/tests/macros.rs Normal file
View File

@ -0,0 +1,6 @@
#[test]
pub fn macros() {
let t = trybuild::TestCases::new();
t.pass("tests/macros/01_trailing_comma.rs");
t.compile_fail("tests/macros/02_metric_name.rs");
}

View File

@ -0,0 +1,11 @@
use metrics::counter;
fn valid_name() {
counter!("abc_def", 1);
}
fn invalid_name() {
counter!("abc$def");
}
fn main() {}

View File

@ -0,0 +1,7 @@
error: metric name must match ^[a-zA-Z_:.][a-zA-Z0-9_:.]*$
--> $DIR/02_metric_name.rs:8:14
|
8 | counter!("abc$def");
| ^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -1,5 +0,0 @@
#[test]
pub fn parsing() {
let t = trybuild::TestCases::new();
t.pass("tests/macros_parsing/*.rs");
}