wip
This commit is contained in:
parent
d72a744a76
commit
7a8f3da859
146
COPYRIGHT
146
COPYRIGHT
|
@ -1,102 +1,94 @@
|
|||
Short version for non-lawyers:
|
||||
|
||||
metrics is MIT licensed.
|
||||
`metrics` is MIT licensed.
|
||||
|
||||
Longer version:
|
||||
|
||||
Copyrights in the metrics project are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the metrics project.
|
||||
Copyrights in the `metrics` project are retained by their contributors. No copyright assignment is
|
||||
required to contribute to the `metrics` project.
|
||||
|
||||
Some files include explicit copyright notices and/or license notices.
|
||||
For full authorship information, see the version control history.
|
||||
Some files include explicit copyright notices and/or license notices. For full authorship
|
||||
information, see the version control history.
|
||||
|
||||
Except as otherwise noted (below and/or in individual files), metrics
|
||||
is licensed under the MIT license <LICENSE> or
|
||||
<http://opensource.org/licenses/MIT>.
|
||||
Except as otherwise noted (below and/or in individual files), `metrics` is licensed under the MIT
|
||||
license <LICENSE> or <http://opensource.org/licenses/MIT>.
|
||||
|
||||
|
||||
metrics includes packages written by third parties.
|
||||
The following third party packages are included, and carry
|
||||
their own copyright notices and license terms:
|
||||
`metrics` includes packages written by third parties. The following third party packages are
|
||||
included, and carry their own copyright notices and license terms:
|
||||
|
||||
* Portions of the API design are derived from tic
|
||||
<https://github.com/brayniac/tic>, which carries the following
|
||||
license:
|
||||
* Portions of the API design are derived from the `tic` crate which carries the following license:
|
||||
|
||||
Copyright (c) 2016 Brian Martin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
* metrics is a fork of rust-lang-nursery/log which carries the following
|
||||
license:
|
||||
* metrics is a fork of `rust-lang-nursery/log` which carries the following license:
|
||||
|
||||
Copyright (c) 2014 The Rust Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
* metrics-observer reuses code from `std::time::Duration` which carries
|
||||
the following license:
|
||||
* metrics-observer reuses code from `std::time::Duration` which carries the following license:
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
* metrics includes code from the `beef` crate which carries the following license:
|
||||
|
||||
Copyright (c) 2020 Maciej Hirsz <hello@maciej.codes>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -80,7 +80,7 @@ impl Generator {
|
|||
impl Drop for Generator {
|
||||
fn drop(&mut self) {
|
||||
info!(
|
||||
" sender latency: min: {:9} p50: {:9} p95: {:9} p99: {:9} p999: {:9} max: {:9}",
|
||||
" sender latency: min: {:8} p50: {:8} p95: {:8} p99: {:8} p999: {:8} max: {:8}",
|
||||
nanos_to_readable(self.hist.min()),
|
||||
nanos_to_readable(self.hist.value_at_percentile(50.0)),
|
||||
nanos_to_readable(self.hist.value_at_percentile(95.0)),
|
||||
|
@ -196,7 +196,7 @@ fn main() {
|
|||
info!("--------------------------------------------------------------------------------");
|
||||
info!(" ingested samples total: {}", total);
|
||||
info!(
|
||||
"snapshot retrieval: min: {:9} p50: {:9} p95: {:9} p99: {:9} p999: {:9} max: {:9}",
|
||||
"snapshot retrieval: min: {:8} p50: {:8} p95: {:8} p99: {:8} p999: {:8} max: {:8}",
|
||||
nanos_to_readable(snapshot_hist.min()),
|
||||
nanos_to_readable(snapshot_hist.value_at_percentile(50.0)),
|
||||
nanos_to_readable(snapshot_hist.value_at_percentile(95.0)),
|
||||
|
|
|
@ -334,7 +334,7 @@ impl PrometheusRecorder {
|
|||
fn add_description_if_missing(&self, key: &Key, description: Option<&'static str>) {
|
||||
if let Some(description) = description {
|
||||
let mut descriptions = self.inner.descriptions.write();
|
||||
if !descriptions.contains_key(key.name().as_ref()) {
|
||||
if !descriptions.contains_key(key.name().to_string().as_str()) {
|
||||
descriptions.insert(key.name().to_string(), description);
|
||||
}
|
||||
}
|
||||
|
@ -552,7 +552,11 @@ fn key_to_parts(key: Key) -> (String, Vec<String>) {
|
|||
let name = key.name();
|
||||
let labels = key.labels();
|
||||
let sanitize = |c| c == '.' || c == '=' || c == '{' || c == '}' || c == '+' || c == '-';
|
||||
let name = name.replace(sanitize, "_");
|
||||
let name = name
|
||||
.parts()
|
||||
.map(|s| s.replace(sanitize, "_"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("_");
|
||||
let labels = labels
|
||||
.into_iter()
|
||||
.map(|label| {
|
||||
|
|
|
@ -224,13 +224,13 @@ pub fn histogram(input: TokenStream) -> TokenStream {
|
|||
|
||||
fn get_expanded_registration(
|
||||
metric_type: &str,
|
||||
key: LitStr,
|
||||
name: LitStr,
|
||||
unit: Option<Expr>,
|
||||
description: Option<LitStr>,
|
||||
labels: Option<Labels>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let register_ident = format_ident!("register_{}", metric_type);
|
||||
let key = key_to_quoted(key, labels);
|
||||
let key = key_to_quoted(labels);
|
||||
|
||||
let unit = match unit {
|
||||
Some(e) => quote! { Some(#e) },
|
||||
|
@ -244,6 +244,7 @@ fn get_expanded_registration(
|
|||
|
||||
quote! {
|
||||
{
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
// Only do this work if there's a recorder installed.
|
||||
if let Some(recorder) = metrics::try_recorder() {
|
||||
// Registrations are fairly rare, don't attempt to cache here
|
||||
|
@ -257,7 +258,7 @@ fn get_expanded_registration(
|
|||
fn get_expanded_callsite<V>(
|
||||
metric_type: &str,
|
||||
op_type: &str,
|
||||
key: LitStr,
|
||||
name: LitStr,
|
||||
labels: Option<Labels>,
|
||||
op_values: V,
|
||||
) -> proc_macro2::TokenStream
|
||||
|
@ -288,7 +289,7 @@ where
|
|||
let labels_len = quote! { #labels_len };
|
||||
|
||||
quote! {
|
||||
static METRIC_NAME: metrics::NameParts = metrics::NameParts::from_static_name(#key);
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
static METRIC_LABELS: [metrics::Label; #labels_len] = [#(#labels),*];
|
||||
static METRIC_KEY: metrics::KeyData =
|
||||
metrics::KeyData::from_static_parts(&METRIC_NAME, &METRIC_LABELS);
|
||||
|
@ -296,7 +297,7 @@ where
|
|||
}
|
||||
None => {
|
||||
quote! {
|
||||
static METRIC_NAME: metrics::NameParts = metrics::NameParts::from_static_name(#key);
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
static METRIC_KEY: metrics::KeyData =
|
||||
metrics::KeyData::from_static_name(&METRIC_NAME);
|
||||
}
|
||||
|
@ -316,9 +317,11 @@ where
|
|||
}
|
||||
} else {
|
||||
// We're on the slow path, so we allocate, womp.
|
||||
let key = key_to_quoted(key, labels);
|
||||
let key = key_to_quoted(labels);
|
||||
quote! {
|
||||
{
|
||||
static METRIC_NAME: [metrics::SharedString; 1] = [metrics::SharedString::const_str(#name)];
|
||||
|
||||
// Only do this work if there's a recorder installed.
|
||||
if let Some(recorder) = metrics::try_recorder() {
|
||||
recorder.#op_ident(metrics::Key::Owned(#key), #op_values);
|
||||
|
@ -355,17 +358,19 @@ fn read_key(input: &mut ParseStream) -> Result<LitStr> {
|
|||
Ok(key)
|
||||
}
|
||||
|
||||
fn key_to_quoted(name: LitStr, labels: Option<Labels>) -> proc_macro2::TokenStream {
|
||||
fn key_to_quoted(labels: Option<Labels>) -> proc_macro2::TokenStream {
|
||||
match labels {
|
||||
None => quote! { metrics::KeyData::from_name(#name) },
|
||||
None => quote! { metrics::KeyData::from_static_name(&METRIC_NAME) },
|
||||
Some(labels) => match labels {
|
||||
Labels::Inline(pairs) => {
|
||||
let labels = pairs
|
||||
.into_iter()
|
||||
.map(|(key, val)| quote! { metrics::Label::new(#key, #val) });
|
||||
quote! { metrics::KeyData::from_parts(#name, vec![#(#labels),*]) }
|
||||
quote! {
|
||||
metrics::KeyData::from_hybrid_parts(&METRIC_NAME, vec![#(#labels),*])
|
||||
}
|
||||
}
|
||||
Labels::Existing(e) => quote! { metrics::KeyData::from_parts(#name, #e) },
|
||||
Labels::Existing(e) => quote! { metrics::KeyData::from_hybrid_parts(&METRIC_NAME, #e) },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,11 @@ fn test_get_expanded_registration() {
|
|||
get_expanded_registration("mytype", parse_quote! { "mykeyname" }, None, None, None);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"None , ",
|
||||
"None",
|
||||
") ; ",
|
||||
|
@ -35,9 +37,11 @@ fn test_get_expanded_registration_with_unit() {
|
|||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"Some (metrics :: Unit :: Nanoseconds) , ",
|
||||
"None",
|
||||
") ; ",
|
||||
|
@ -59,9 +63,11 @@ fn test_get_expanded_registration_with_description() {
|
|||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"None , ",
|
||||
"Some (\"flerkin\")",
|
||||
") ; ",
|
||||
|
@ -84,9 +90,11 @@ fn test_get_expanded_registration_with_unit_and_description() {
|
|||
);
|
||||
|
||||
let expected = concat!(
|
||||
"{ if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . register_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_name (\"mykeyname\")) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_static_name (& METRIC_NAME)) , ",
|
||||
"Some (metrics :: Unit :: Nanoseconds) , ",
|
||||
"Some (\"flerkin\")",
|
||||
") ; ",
|
||||
|
@ -108,7 +116,8 @@ fn test_get_expanded_callsite_fast_path_no_labels() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_name (\"mykeyname\") ; ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_name (& METRIC_NAME) ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Borrowed (& METRIC_KEY) , 1) ; ",
|
||||
"} }",
|
||||
|
@ -130,8 +139,9 @@ fn test_get_expanded_callsite_fast_path_static_labels() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"static METRIC_LABELS : [metrics :: Label ; 1usize] = [metrics :: Label :: from_static_parts (\"key1\" , \"value1\")] ; ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_parts (\"mykeyname\" , & METRIC_LABELS) ; ",
|
||||
"static METRIC_KEY : metrics :: KeyData = metrics :: KeyData :: from_static_parts (& METRIC_NAME , & METRIC_LABELS) ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Borrowed (& METRIC_KEY) , 1) ; ",
|
||||
"} ",
|
||||
|
@ -154,9 +164,10 @@ fn test_get_expanded_callsite_fast_path_dynamic_labels() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (metrics :: Key :: Owned (",
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [metrics :: Label :: new (\"key1\" , & value1)])",
|
||||
"metrics :: KeyData :: from_hybrid_parts (& METRIC_NAME , vec ! [metrics :: Label :: new (\"key1\" , & value1)])",
|
||||
") , 1) ; ",
|
||||
"} ",
|
||||
"}",
|
||||
|
@ -178,9 +189,10 @@ fn test_get_expanded_callsite_regular_path() {
|
|||
|
||||
let expected = concat!(
|
||||
"{ ",
|
||||
"static METRIC_NAME : [metrics :: SharedString ; 1] = [metrics :: SharedString :: const_str (\"mykeyname\")] ; ",
|
||||
"if let Some (recorder) = metrics :: try_recorder () { ",
|
||||
"recorder . myop_mytype (",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_parts (\"mykeyname\" , mylabels)) , ",
|
||||
"metrics :: Key :: Owned (metrics :: KeyData :: from_hybrid_parts (& METRIC_NAME , mylabels)) , ",
|
||||
"1",
|
||||
") ; ",
|
||||
"} }",
|
||||
|
@ -191,18 +203,17 @@ fn test_get_expanded_callsite_regular_path() {
|
|||
|
||||
#[test]
|
||||
fn test_key_to_quoted_no_labels() {
|
||||
let stream = key_to_quoted(parse_quote! {"mykeyname"}, None);
|
||||
let expected = "metrics :: KeyData :: from_name (\"mykeyname\")";
|
||||
let stream = key_to_quoted(None);
|
||||
let expected = "metrics :: KeyData :: from_static_name (& METRIC_NAME)";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_to_quoted_existing_labels() {
|
||||
let stream = key_to_quoted(
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(Labels::Existing(Expr::Path(parse_quote! { mylabels }))),
|
||||
);
|
||||
let expected = "metrics :: KeyData :: from_parts (\"mykeyname\" , mylabels)";
|
||||
let stream = key_to_quoted(Some(Labels::Existing(Expr::Path(
|
||||
parse_quote! { mylabels },
|
||||
))));
|
||||
let expected = "metrics :: KeyData :: from_hybrid_parts (& METRIC_NAME , mylabels)";
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
||||
|
@ -210,15 +221,12 @@ fn test_key_to_quoted_existing_labels() {
|
|||
/// Key).
|
||||
#[test]
|
||||
fn test_key_to_quoted_inline_labels() {
|
||||
let stream = key_to_quoted(
|
||||
parse_quote! {"mykeyname"},
|
||||
Some(Labels::Inline(vec![
|
||||
(parse_quote! {"mylabel1"}, parse_quote! { mylabel1 }),
|
||||
(parse_quote! {"mylabel2"}, parse_quote! { "mylabel2" }),
|
||||
])),
|
||||
);
|
||||
let stream = key_to_quoted(Some(Labels::Inline(vec![
|
||||
(parse_quote! {"mylabel1"}, parse_quote! { mylabel1 }),
|
||||
(parse_quote! {"mylabel2"}, parse_quote! { "mylabel2" }),
|
||||
])));
|
||||
let expected = concat!(
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [",
|
||||
"metrics :: KeyData :: from_hybrid_parts (& METRIC_NAME , vec ! [",
|
||||
"metrics :: Label :: new (\"mylabel1\" , mylabel1) , ",
|
||||
"metrics :: Label :: new (\"mylabel2\" , \"mylabel2\")",
|
||||
"])"
|
||||
|
@ -228,10 +236,7 @@ fn test_key_to_quoted_inline_labels() {
|
|||
|
||||
#[test]
|
||||
fn test_key_to_quoted_inline_labels_empty() {
|
||||
let stream = key_to_quoted(parse_quote! {"mykeyname"}, Some(Labels::Inline(vec![])));
|
||||
let expected = concat!(
|
||||
"metrics :: KeyData :: from_parts (\"mykeyname\" , vec ! [",
|
||||
"])"
|
||||
);
|
||||
let stream = key_to_quoted(Some(Labels::Inline(vec![])));
|
||||
let expected = concat!("metrics :: KeyData :: from_hybrid_parts (& METRIC_NAME , vec ! [])");
|
||||
assert_eq!(stream.to_string(), expected);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label, NoopRecorder, Recorder};
|
||||
use metrics::{Key, KeyData, Label, NoopRecorder, Recorder, SharedString};
|
||||
use metrics_tracing_context::{MetricsLayer, TracingContextLayer};
|
||||
use metrics_util::layers::Layer;
|
||||
use tracing::{
|
||||
|
@ -22,8 +22,9 @@ fn layer_benchmark(c: &mut Criterion) {
|
|||
|
||||
let tracing_layer = TracingContextLayer::all();
|
||||
let recorder = tracing_layer.layer(NoopRecorder);
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("key", &LABELS);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
|
@ -32,8 +33,9 @@ fn layer_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
.with_function("noop recorder overhead (increment_counter)", |b| {
|
||||
let recorder = NoopRecorder;
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts("key", &LABELS);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
|
|
|
@ -101,7 +101,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// [`TracingContext`] is a [`metrics::Recorder`] that injects labels from [`tracing::Span`]s.
|
||||
/// [`TracingContext`] is a [`metrics::Recorder`] that injects labels from[`tracing::Span`]s.
|
||||
pub struct TracingContext<R, F> {
|
||||
inner: R,
|
||||
label_filter: F,
|
||||
|
@ -126,7 +126,7 @@ where
|
|||
fn enhance_key(&self, key: Key) -> Key {
|
||||
let (name, mut labels) = key.into_owned().into_parts();
|
||||
self.enhance_labels(&mut labels);
|
||||
KeyData::from_parts(name, labels).into()
|
||||
KeyData::from_owned_parts(name, labels).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ fn test_basic_functionality() {
|
|||
snapshot,
|
||||
vec![(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"login_attempts",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
@ -95,7 +95,7 @@ fn test_macro_forms() {
|
|||
vec![
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"login_attempts_no_labels",
|
||||
vec![
|
||||
Label::new("user", "ferris"),
|
||||
|
@ -109,7 +109,7 @@ fn test_macro_forms() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"login_attempts_static_labels",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
@ -124,7 +124,7 @@ fn test_macro_forms() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"login_attempts_dynamic_labels",
|
||||
vec![
|
||||
Label::new("node_name", "localhost"),
|
||||
|
@ -139,7 +139,7 @@ fn test_macro_forms() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"login_attempts_static_and_dynamic_labels",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
@ -224,7 +224,7 @@ fn test_multiple_paths_to_the_same_callsite() {
|
|||
vec![
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"my_counter",
|
||||
vec![
|
||||
Label::new("shared_field", "path1"),
|
||||
|
@ -239,7 +239,7 @@ fn test_multiple_paths_to_the_same_callsite() {
|
|||
),
|
||||
(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"my_counter",
|
||||
vec![
|
||||
Label::new("shared_field", "path2"),
|
||||
|
@ -295,7 +295,7 @@ fn test_nested_spans() {
|
|||
snapshot,
|
||||
vec![(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"my_counter",
|
||||
vec![
|
||||
Label::new("shared_field", "inner"),
|
||||
|
@ -340,7 +340,7 @@ fn test_label_filtering() {
|
|||
snapshot,
|
||||
vec![(
|
||||
MetricKind::Counter,
|
||||
KeyData::from_parts(
|
||||
KeyData::from_owned_parts(
|
||||
"login_attempts",
|
||||
vec![
|
||||
Label::new("service", "login_service"),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label, NameParts, NoopRecorder, Recorder};
|
||||
use metrics::{Key, KeyData, Label, NoopRecorder, Recorder, SharedString};
|
||||
use metrics_util::layers::{Layer, PrefixLayer};
|
||||
|
||||
fn layer_benchmark(c: &mut Criterion) {
|
||||
|
@ -8,9 +8,9 @@ fn layer_benchmark(c: &mut Criterion) {
|
|||
Benchmark::new("basic", |b| {
|
||||
let prefix_layer = PrefixLayer::new("prefix");
|
||||
let recorder = prefix_layer.layer(NoopRecorder);
|
||||
static NAME: NameParts = NameParts::from_static_name("key");
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&NAME, &LABELS);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
|
@ -18,9 +18,9 @@ fn layer_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
.with_function("noop recorder overhead (increment_counter)", |b| {
|
||||
let recorder = NoopRecorder;
|
||||
static NAME: NameParts = NameParts::from_static_name("key");
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&NAME, &LABELS);
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
b.iter(|| {
|
||||
recorder.increment_counter(Key::Borrowed(&KEY_DATA), 1);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use criterion::{criterion_group, criterion_main, BatchSize, Benchmark, Criterion};
|
||||
use metrics::{Key, KeyData, Label, NameParts};
|
||||
use metrics::{Key, KeyData, Label, SharedString};
|
||||
use metrics_util::Registry;
|
||||
|
||||
fn registry_benchmark(c: &mut Criterion) {
|
||||
|
@ -7,7 +7,7 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
"registry",
|
||||
Benchmark::new("cached op (basic)", |b| {
|
||||
let registry: Registry<Key, ()> = Registry::new();
|
||||
static KEY_NAME: NameParts = NameParts::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name(&KEY_NAME);
|
||||
|
||||
b.iter(|| {
|
||||
|
@ -17,7 +17,7 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
.with_function("cached op (labels)", |b| {
|
||||
let registry: Registry<Key, ()> = Registry::new();
|
||||
static KEY_NAME: NameParts = NameParts::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
|
||||
|
@ -64,18 +64,18 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
let labels = vec![Label::new("type", "http")];
|
||||
KeyData::from_parts(key, labels)
|
||||
KeyData::from_owned_parts(key, labels)
|
||||
})
|
||||
})
|
||||
.with_function("const key data overhead (basic)", |b| {
|
||||
b.iter(|| {
|
||||
static KEY_NAME: NameParts = NameParts::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
KeyData::from_static_name(&KEY_NAME)
|
||||
})
|
||||
})
|
||||
.with_function("const key data overhead (labels)", |b| {
|
||||
b.iter(|| {
|
||||
static KEY_NAME: NameParts = NameParts::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
KeyData::from_static_parts(&KEY_NAME, &LABELS)
|
||||
})
|
||||
|
@ -90,16 +90,16 @@ fn registry_benchmark(c: &mut Criterion) {
|
|||
b.iter(|| {
|
||||
let key = "simple_key";
|
||||
let labels = vec![Label::new("type", "http")];
|
||||
Key::Owned(KeyData::from_parts(key, labels))
|
||||
Key::Owned(KeyData::from_owned_parts(key, labels))
|
||||
})
|
||||
})
|
||||
.with_function("cached key overhead (basic)", |b| {
|
||||
static KEY_NAME: NameParts = NameParts::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_name(&KEY_NAME);
|
||||
b.iter(|| Key::Borrowed(&KEY_DATA))
|
||||
})
|
||||
.with_function("cached key overhead (labels)", |b| {
|
||||
static KEY_NAME: NameParts = NameParts::from_static_name("simple_key");
|
||||
static KEY_NAME: [SharedString; 1] = [SharedString::const_str("simple_key")];
|
||||
static KEY_LABELS: [Label; 1] = [Label::from_static_parts("type", "http")];
|
||||
static KEY_DATA: KeyData = KeyData::from_static_parts(&KEY_NAME, &KEY_LABELS);
|
||||
b.iter(|| Key::Borrowed(&KEY_DATA))
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
use crate::layers::Layer;
|
||||
use metrics::{Key, Recorder, Unit};
|
||||
use metrics::{Key, Recorder, SharedString, Unit};
|
||||
|
||||
/// Applies a prefix to every metric key.
|
||||
///
|
||||
/// Keys will be prefixed in the format of `<prefix>.<remaining>`.
|
||||
pub struct Prefix<R> {
|
||||
prefix: &'static str,
|
||||
prefix: SharedString,
|
||||
inner: R,
|
||||
}
|
||||
|
||||
impl<R> Prefix<R> {
|
||||
fn prefix_key(&self, key: Key) -> Key {
|
||||
let mut owned = key.into_owned();
|
||||
owned.prepend_name(self.prefix);
|
||||
owned.into()
|
||||
key.into_owned().prepend_name(self.prefix.clone()).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +64,7 @@ impl<R> Layer<R> for PrefixLayer {
|
|||
|
||||
fn layer(&self, inner: R) -> Self::Output {
|
||||
Prefix {
|
||||
prefix: self.0,
|
||||
prefix: self.0.into(),
|
||||
inner,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
//! This example is purely for development.
|
||||
use metrics::{Key, KeyData, Label, NameParts, SharedString};
|
||||
use std::borrow::Cow;
|
||||
use metrics::{Key, KeyData, NameParts, Label, SharedString};
|
||||
|
||||
fn main() {
|
||||
println!("KeyData: {} bytes", std::mem::size_of::<KeyData>());
|
||||
println!("Key: {} bytes", std::mem::size_of::<Key>());
|
||||
println!("NameParts: {} bytes", std::mem::size_of::<NameParts>());
|
||||
println!("Label: {} bytes", std::mem::size_of::<Label>());
|
||||
println!("Cow<'static, [Label]>: {} bytes", std::mem::size_of::<Cow<'static, [Label]>>());
|
||||
println!("Vec<SharedString>: {} bytes", std::mem::size_of::<Vec<SharedString>>());
|
||||
println!("[Option<SharedString>; 2]: {} bytes", std::mem::size_of::<[Option<SharedString>; 2]>());
|
||||
println!(
|
||||
"Cow<'static, [Label]>: {} bytes",
|
||||
std::mem::size_of::<Cow<'static, [Label]>>()
|
||||
);
|
||||
println!(
|
||||
"Vec<SharedString>: {} bytes",
|
||||
std::mem::size_of::<Vec<SharedString>>()
|
||||
);
|
||||
println!(
|
||||
"[Option<SharedString>; 2]: {} bytes",
|
||||
std::mem::size_of::<[Option<SharedString>; 2]>()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
#[cfg(target_pointer_width = "64")]
|
||||
use beef::lean::Cow;
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
use beef::Cow;
|
||||
use crate::cow::Cow;
|
||||
|
||||
/// An allocation-optimized string.
|
||||
///
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{IntoLabels, Label, SharedString};
|
||||
use alloc::{borrow::Cow, string::String, vec::Vec};
|
||||
use crate::{cow::Cow, IntoLabels, Label, SharedString};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
|
@ -7,147 +7,81 @@ use core::{
|
|||
slice::Iter,
|
||||
};
|
||||
|
||||
const NO_LABELS: [Label; 0] = [];
|
||||
|
||||
/// Parts compromising a metric name.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub enum NameParts {
|
||||
/// Optimized path for inline storage.
|
||||
///
|
||||
/// This variant will be used primarily when the metric name is not scoped, and has not been
|
||||
/// modified via `append`/`prepend`.
|
||||
Inline([Option<SharedString>; 2]),
|
||||
/// Dynamic number of name parts.
|
||||
///
|
||||
/// If we do not have an open slot for appending/prepending, this variant will be used.
|
||||
Dynamic(Vec<SharedString>),
|
||||
}
|
||||
pub struct NameParts(Cow<'static, [SharedString]>);
|
||||
|
||||
impl NameParts {
|
||||
/// Creates a [`NameParts`] from the given name.
|
||||
pub fn from_name<N: Into<SharedString>>(name: N) -> Self {
|
||||
NameParts::Inline([Some(name.into()), None])
|
||||
NameParts(Cow::owned(vec![name.into()]))
|
||||
}
|
||||
|
||||
/// Creates a [`NameParts`] from the given static name.
|
||||
pub const fn from_static_name(name: &'static str) -> Self {
|
||||
NameParts::Inline([Some(SharedString::const_str(name)), None])
|
||||
}
|
||||
|
||||
/// Creates a [`NameParts`] from the given static name.
|
||||
pub const fn from_static_names(first: &'static str, second: &'static str) -> Self {
|
||||
NameParts::Inline([
|
||||
Some(SharedString::const_str(first)),
|
||||
Some(SharedString::const_str(second)),
|
||||
])
|
||||
pub const fn from_static_names(names: &'static [SharedString]) -> Self {
|
||||
NameParts(Cow::<'static, [SharedString]>::const_slice(names))
|
||||
}
|
||||
|
||||
/// Appends a name part.
|
||||
pub fn append<S: Into<SharedString>>(&mut self, part: S) {
|
||||
match *self {
|
||||
NameParts::Inline(ref mut inner) => {
|
||||
if inner[1].is_none() {
|
||||
// Open slot, so we can utilize it.
|
||||
inner[1] = Some(part.into());
|
||||
} else {
|
||||
// Have to spill over.
|
||||
let mut parts = Vec::with_capacity(4);
|
||||
parts.push(inner[0].clone().unwrap());
|
||||
parts.push(inner[1].clone().unwrap());
|
||||
parts.push(part.into());
|
||||
*self = NameParts::Dynamic(parts);
|
||||
}
|
||||
},
|
||||
NameParts::Dynamic(ref mut parts) => {
|
||||
parts.push(part.into())
|
||||
},
|
||||
}
|
||||
pub fn append<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let mut parts = self.0.into_owned();
|
||||
parts.push(part.into());
|
||||
NameParts(Cow::owned(parts))
|
||||
}
|
||||
|
||||
/// Prepends a name part.
|
||||
pub fn prepend<S: Into<SharedString>>(&mut self, part: S) {
|
||||
match *self {
|
||||
NameParts::Inline(ref mut inner) => {
|
||||
if inner[1].is_none() {
|
||||
// Open slot, so we can utilize it.
|
||||
inner[1] = inner[0].take();
|
||||
inner[0] = Some(part.into());
|
||||
} else {
|
||||
// Have to spill over.
|
||||
let mut parts = Vec::with_capacity(4);
|
||||
parts.push(part.into());
|
||||
parts.push(inner[0].clone().unwrap());
|
||||
parts.push(inner[1].clone().unwrap());
|
||||
*self = NameParts::Dynamic(parts);
|
||||
}
|
||||
},
|
||||
NameParts::Dynamic(ref mut parts) => {
|
||||
parts.insert(0, part.into())
|
||||
},
|
||||
}
|
||||
pub fn prepend<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let mut parts = self.0.into_owned();
|
||||
parts.insert(0, part.into());
|
||||
NameParts(Cow::owned(parts))
|
||||
}
|
||||
|
||||
/// Gets a reference to the parts for this name.
|
||||
pub fn parts(&self) -> PartsIter {
|
||||
PartsIter::from(self)
|
||||
pub fn parts(&self) -> Iter<'_, SharedString> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Renders the name parts as a dot-delimited string.
|
||||
pub fn to_string(&self) -> String {
|
||||
// This is suboptimal since we're allocating in a bunch of ways.
|
||||
//
|
||||
// Might be faster to figure out the string length and then allocate a single string with
|
||||
// the required capacity, and write into it, potentially pooling them? Dunno, we should
|
||||
// actually benchmark this. :P
|
||||
self.0
|
||||
.iter()
|
||||
.map(|s| s.as_ref())
|
||||
.collect::<Vec<_>>()
|
||||
.join(".")
|
||||
}
|
||||
}
|
||||
|
||||
enum PartsIterInner<'a> {
|
||||
Inline(&'a [Option<SharedString>; 2]),
|
||||
Dynamic(Iter<'a, SharedString>),
|
||||
}
|
||||
|
||||
/// Name parts iterator.
|
||||
pub struct PartsIter<'a> {
|
||||
idx: usize,
|
||||
inner: PartsIterInner<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a NameParts> for PartsIter<'a> {
|
||||
fn from(parts: &'a NameParts) -> Self {
|
||||
let inner = match parts {
|
||||
NameParts::Inline(inner) => PartsIterInner::Inline(inner),
|
||||
NameParts::Dynamic(parts) => PartsIterInner::Dynamic(parts.iter()),
|
||||
};
|
||||
PartsIter { idx: 0, inner }
|
||||
impl From<String> for NameParts {
|
||||
fn from(name: String) -> NameParts {
|
||||
NameParts::from_name(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PartsIter<'a> {
|
||||
type Item = &'a SharedString;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.inner {
|
||||
PartsIterInner::Inline(inner) => {
|
||||
if self.idx > 1 { return None }
|
||||
|
||||
let item = inner[self.idx].as_ref();
|
||||
self.idx += 1;
|
||||
item
|
||||
},
|
||||
PartsIterInner::Dynamic(ref mut iter) => iter.next(),
|
||||
}
|
||||
impl From<&'static str> for NameParts {
|
||||
fn from(name: &'static str) -> NameParts {
|
||||
NameParts::from_name(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NameParts {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
NameParts::Inline(inner) => if inner[1].is_none() {
|
||||
write!(f, "{}", inner[0].as_ref().unwrap())
|
||||
let mut first = true;
|
||||
for s in self.parts() {
|
||||
if !first {
|
||||
write!(f, ".{}", s)?;
|
||||
first = false;
|
||||
} else {
|
||||
write!(f, "{}.{}", inner[0].as_ref().unwrap(), inner[1].as_ref().unwrap())
|
||||
},
|
||||
NameParts::Dynamic(parts) => {
|
||||
for (i, s) in parts.iter().enumerate() {
|
||||
if i != parts.len() - 1 {
|
||||
write!(f, "{}.", s)?;
|
||||
} else {
|
||||
write!(f, "{}", s)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
write!(f, "{}", s)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +92,7 @@ impl fmt::Display for NameParts {
|
|||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct KeyData {
|
||||
// TODO: once const slicing is possible on stable, we could likely use `beef` for both of these
|
||||
name_parts: Cow<'static, NameParts>,
|
||||
name_parts: NameParts,
|
||||
labels: Cow<'static, [Label]>,
|
||||
}
|
||||
|
||||
|
@ -168,38 +102,52 @@ impl KeyData {
|
|||
where
|
||||
N: Into<SharedString>,
|
||||
{
|
||||
Self::from_parts(name, Vec::new())
|
||||
Self {
|
||||
name_parts: NameParts::from_name(name),
|
||||
labels: Cow::owned(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a name and vector of [`Label`]s.
|
||||
pub fn from_parts<N, L>(name: N, labels: L) -> Self
|
||||
/// Creates a [`KeyData`] from a name.
|
||||
pub fn from_owned_parts<N, L>(name: N, labels: L) -> Self
|
||||
where
|
||||
N: Into<SharedString>,
|
||||
N: Into<NameParts>,
|
||||
L: IntoLabels,
|
||||
{
|
||||
Self {
|
||||
name_parts: Cow::Owned(NameParts::from_name(name)),
|
||||
labels: labels.into_labels().into(),
|
||||
name_parts: name.into(),
|
||||
labels: Cow::owned(labels.into_labels()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a name and vector of [`Label`]s.
|
||||
pub fn from_hybrid_parts<L>(name_parts: &'static [SharedString], labels: L) -> Self
|
||||
where
|
||||
L: IntoLabels,
|
||||
{
|
||||
Self {
|
||||
name_parts: NameParts::from_static_names(name_parts),
|
||||
labels: Cow::owned(labels.into_labels()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a static name.
|
||||
///
|
||||
/// This function is `const`, so it can be used in a static context.
|
||||
pub const fn from_static_name(name_parts: &'static NameParts) -> Self {
|
||||
Self {
|
||||
name_parts: Cow::Borrowed(name_parts),
|
||||
labels: Cow::Owned(Vec::new()),
|
||||
}
|
||||
pub const fn from_static_name(name_parts: &'static [SharedString]) -> Self {
|
||||
Self::from_static_parts(name_parts, &NO_LABELS)
|
||||
}
|
||||
|
||||
/// Creates a [`KeyData`] from a static name and static set of labels.
|
||||
///
|
||||
/// This function is `const`, so it can be used in a static context.
|
||||
pub const fn from_static_parts(name_parts: &'static NameParts, labels: &'static [Label]) -> Self {
|
||||
pub const fn from_static_parts(
|
||||
name_parts: &'static [SharedString],
|
||||
labels: &'static [Label],
|
||||
) -> Self {
|
||||
Self {
|
||||
name_parts: Cow::Borrowed(name_parts),
|
||||
labels: Cow::Borrowed(labels),
|
||||
name_parts: NameParts::from_static_names(name_parts),
|
||||
labels: Cow::<[Label]>::const_slice(labels),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,18 +162,26 @@ impl KeyData {
|
|||
}
|
||||
|
||||
/// Appends a part to the name,
|
||||
pub fn append_name<S: Into<SharedString>>(&mut self, part: S) {
|
||||
self.name_parts.to_mut().append(part)
|
||||
pub fn append_name<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let name_parts = self.name_parts.append(part);
|
||||
Self {
|
||||
name_parts,
|
||||
labels: self.labels,
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepends a part to the name.
|
||||
pub fn prepend_name<S: Into<SharedString>>(&mut self, part: S) {
|
||||
self.name_parts.to_mut().prepend(part)
|
||||
pub fn prepend_name<S: Into<SharedString>>(self, part: S) -> Self {
|
||||
let name_parts = self.name_parts.prepend(part);
|
||||
Self {
|
||||
name_parts,
|
||||
labels: self.labels,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes this [`Key`], returning the name parts and any labels.
|
||||
pub fn into_parts(self) -> (NameParts, Vec<Label>) {
|
||||
(self.name_parts.into_owned(), self.labels.into_owned())
|
||||
(self.name_parts.clone(), self.labels.into_owned())
|
||||
}
|
||||
|
||||
/// Clones this [`Key`], and expands the existing set of labels.
|
||||
|
@ -283,7 +239,10 @@ where
|
|||
L: IntoLabels,
|
||||
{
|
||||
fn from(parts: (N, L)) -> Self {
|
||||
Self::from_parts(parts.0, parts.1)
|
||||
Self {
|
||||
name_parts: NameParts::from_name(parts.0),
|
||||
labels: Cow::owned(parts.1.into_labels()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,11 +342,12 @@ impl From<&'static KeyData> for Key {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Key, KeyData, NameParts};
|
||||
use crate::Label;
|
||||
use super::{Key, KeyData};
|
||||
use crate::{Label, SharedString};
|
||||
use std::collections::HashMap;
|
||||
|
||||
static BORROWED_NAME: NameParts = NameParts::from_static_name("name");
|
||||
static BORROWED_NAME: [SharedString; 1] = [SharedString::const_str("name")];
|
||||
static FOOBAR_NAME: [SharedString; 1] = [SharedString::const_str("foobar")];
|
||||
static BORROWED_BASIC: KeyData = KeyData::from_static_name(&BORROWED_NAME);
|
||||
static LABELS: [Label; 1] = [Label::from_static_parts("key", "value")];
|
||||
static BORROWED_LABELS: KeyData = KeyData::from_static_parts(&BORROWED_NAME, &LABELS);
|
||||
|
@ -406,7 +366,7 @@ mod tests {
|
|||
assert_eq!(previous, Some(&42));
|
||||
|
||||
let labels = LABELS.to_vec();
|
||||
let owned_labels = KeyData::from_parts("name", labels);
|
||||
let owned_labels = KeyData::from_hybrid_parts(&BORROWED_NAME, labels);
|
||||
assert_eq!(&owned_labels, &BORROWED_LABELS);
|
||||
|
||||
let previous = keys.insert(owned_labels, 43);
|
||||
|
@ -431,7 +391,7 @@ mod tests {
|
|||
assert_eq!(previous, Some(&42));
|
||||
|
||||
let labels = LABELS.to_vec();
|
||||
let owned_labels = Key::from(KeyData::from_parts("name", labels));
|
||||
let owned_labels = Key::from(KeyData::from_hybrid_parts(&BORROWED_NAME, labels));
|
||||
let borrowed_labels = Key::from(&BORROWED_LABELS);
|
||||
assert_eq!(owned_labels, borrowed_labels);
|
||||
|
||||
|
@ -448,19 +408,19 @@ mod tests {
|
|||
let result1 = key1.to_string();
|
||||
assert_eq!(result1, "KeyData(foobar)");
|
||||
|
||||
let key2 = KeyData::from_parts("foobar", vec![Label::new("system", "http")]);
|
||||
let key2 = KeyData::from_hybrid_parts(&FOOBAR_NAME, vec![Label::new("system", "http")]);
|
||||
let result2 = key2.to_string();
|
||||
assert_eq!(result2, "KeyData(foobar, [system = http])");
|
||||
|
||||
let key3 = KeyData::from_parts(
|
||||
"foobar",
|
||||
let key3 = KeyData::from_hybrid_parts(
|
||||
&FOOBAR_NAME,
|
||||
vec![Label::new("system", "http"), Label::new("user", "joe")],
|
||||
);
|
||||
let result3 = key3.to_string();
|
||||
assert_eq!(result3, "KeyData(foobar, [system = http, user = joe])");
|
||||
|
||||
let key4 = KeyData::from_parts(
|
||||
"foobar",
|
||||
let key4 = KeyData::from_hybrid_parts(
|
||||
&FOOBAR_NAME,
|
||||
vec![
|
||||
Label::new("black", "black"),
|
||||
Label::new("lives", "lives"),
|
||||
|
@ -479,10 +439,9 @@ mod tests {
|
|||
let owned_a = KeyData::from_name("a");
|
||||
let owned_b = KeyData::from_name("b");
|
||||
|
||||
|
||||
static A_NAME: NameParts = NameParts::from_static_name("a");
|
||||
static A_NAME: [SharedString; 1] = [SharedString::const_str("a")];
|
||||
static STATIC_A: KeyData = KeyData::from_static_name(&A_NAME);
|
||||
static B_NAME: NameParts = NameParts::from_static_name("b");
|
||||
static B_NAME: [SharedString; 1] = [SharedString::const_str("b")];
|
||||
static STATIC_B: KeyData = KeyData::from_static_name(&B_NAME);
|
||||
|
||||
assert_eq!(Key::Owned(owned_a.clone()), Key::Owned(owned_a.clone()));
|
||||
|
|
|
@ -223,6 +223,8 @@ use proc_macro_hack::proc_macro_hack;
|
|||
mod common;
|
||||
pub use self::common::*;
|
||||
|
||||
mod cow;
|
||||
|
||||
mod key;
|
||||
pub use self::key::*;
|
||||
|
||||
|
|
Loading…
Reference in New Issue