mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-05-03 10:40:03 +00:00
Use leon for template in binstalk & detect malformed pkg-url/pkg-fmt early (#933)
Fixed #851 * Add new dep leon to crate binstalk * Add new variant `BinstallError::Template{Parse, Render}Error` * Use `leon::Template` in mod `bins` * Use `leon::Template` in mod `fetchers::gh_crate_meta` * Refactor mod `bins`: Rm unused associated fn & fields from `Context` * Simplify unit testing in mod `fetchers::gh_crate_meta` * Rm soft-deprecated field `fetchers::gh_crate_meta::Context::format` and change the `match` to resolve `archive` => `self.archive_format`. * Make macro_rules `leon::template!` far easier to use * Construct `leon::Template<'_>` as constant in `gh_crate_meta::hosting` * Simplify `leon::Values` trait Change its method `get_value` signature to ```rust fn get_value(&self, key: &str) -> Option<Cow<'_, str>>; ``` Now, `ValuesFn` also accepts non-`'static` function, but now `leon::Values` is only implemented for `&ValuesFn<F>` now. This makes it a little bit more cumbersome to use but I think it's a reasonable drawback. * Rm `Send` bound req from `ValuesFn` * Impl new fn `leon::Template::cast` for casting `Template<'s>` to `Template<'t>` where `'s: 't` * Rename `leon::Template::has_keys` => `has_any_of_keys` * Make checking whether format related keys are present more robust * Optimize `GhCrateMeta::launch_baseline_find_tasks`: Skip checking all fmt ext if none of the format related keys ("format", "archive-format", "archive-suffix") are present. * Only ret `.exe` in `PkgFmt::extensions` on windows by adding a new param `is_windows: bool` * Improve debug msg in `GhCrateMeta::fetch_and_extract` * Add warnings to `GhCrateMeta::find` * Rm dep tinytemplate * `impl<'s, 'rhs: 's> ops::AddAssign<&Template<'rhs>> for Template<'s>` * `impl<'s, 'rhs: 's> ops::AddAssign<Template<'rhs>> for Template<'s>` * `impl<'s, 'item: 's> ops::AddAssign<Item<'item>> for Template<'s>` * `impl<'s, 'item: 's> ops::AddAssign<&Item<'item>> for Template<'s>` * `impl<'s, 'rhs: 's> ops::Add<Template<'rhs>> for Template<'s>` (improved existing `Add` impl) * `impl<'s, 'rhs: 's> ops::Add<&Template<'rhs>> for Template<'s>` * `impl<'s, 'item: 's> ops::Add<Item<'item>> for Template<'s>` * `impl<'s, 'item: 's> ops::Add<&Item<'item>> for Template<'s>` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> Co-authored-by: Félix Saparelli <felix@passcod.name>
This commit is contained in:
parent
47d4aeaa96
commit
a27d5aebf6
13 changed files with 512 additions and 312 deletions
|
@ -59,7 +59,7 @@
|
|||
//! # let template = Template::parse("hello {name}").unwrap();
|
||||
//! assert_eq!(
|
||||
//! template.render(
|
||||
//! &vals(|_key| Some("marcus".into()))
|
||||
//! &&vals(|_key| Some("marcus".into()))
|
||||
//! ).unwrap().as_str(),
|
||||
//! "hello marcus",
|
||||
//! );
|
||||
|
@ -76,7 +76,7 @@
|
|||
//! let mut buf: Vec<u8> = Vec::new();
|
||||
//! template.render_into(
|
||||
//! &mut buf,
|
||||
//! &vals(|key| if key == "name" {
|
||||
//! &&vals(|key| if key == "name" {
|
||||
//! Some("julius".into())
|
||||
//! } else {
|
||||
//! None
|
||||
|
@ -107,7 +107,7 @@
|
|||
//! name: &'static str,
|
||||
//! }
|
||||
//! impl Values for MyMap {
|
||||
//! fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
//! fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
//! if key == "name" {
|
||||
//! Some(self.name.into())
|
||||
//! } else {
|
||||
|
|
|
@ -1,4 +1,35 @@
|
|||
/// Construct a template constant without needing to make an items constant.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __template_item {
|
||||
() => {};
|
||||
({ $key:literal }) => {
|
||||
$crate::Item::Key($key)
|
||||
};
|
||||
( $text:literal ) => {
|
||||
$crate::Item::Text($text)
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __template_impl {
|
||||
($( $token:tt ),* ; $default:expr) => {
|
||||
$crate::Template::new(
|
||||
{
|
||||
const ITEMS: &'static [$crate::Item<'static>] = &[
|
||||
$(
|
||||
$crate::__template_item!($token)
|
||||
),*
|
||||
];
|
||||
ITEMS
|
||||
},
|
||||
$default,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Construct a template constant using syntax similar to the template to be
|
||||
/// passed to [`Template::parse`].
|
||||
///
|
||||
/// This is essentially a shorthand for:
|
||||
///
|
||||
|
@ -13,9 +44,8 @@
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use leon::Item::*;
|
||||
/// assert_eq!(
|
||||
/// leon::template!(Text("Hello "), Key("name"))
|
||||
/// leon::template!("Hello ", {"name"})
|
||||
/// .render(&[("name", "Магда Нахман")])
|
||||
/// .unwrap(),
|
||||
/// "Hello Магда Нахман",
|
||||
|
@ -25,9 +55,8 @@
|
|||
/// With a default:
|
||||
///
|
||||
/// ```
|
||||
/// use leon::Item::*;
|
||||
/// assert_eq!(
|
||||
/// leon::template!(Text("Hello "), Key("name"); "M. P. T. Acharya")
|
||||
/// leon::template!("Hello ", {"name"}; "M. P. T. Acharya")
|
||||
/// .render(&[("city", "Madras")])
|
||||
/// .unwrap(),
|
||||
/// "Hello M. P. T. Acharya",
|
||||
|
@ -35,16 +64,93 @@
|
|||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! template {
|
||||
($($item:expr),* $(,)?) => {
|
||||
$crate::Template::new({
|
||||
const ITEMS: &'static [$crate::Item<'static>] = &[$($item),*];
|
||||
ITEMS
|
||||
}, ::core::option::Option::None)
|
||||
() => {
|
||||
$crate::Template::new(
|
||||
{
|
||||
const ITEMS: &'static [$crate::Item<'static>] = &[];
|
||||
ITEMS
|
||||
},
|
||||
::core::option::Option::None,
|
||||
)
|
||||
};
|
||||
($($item:expr),* $(,)? ; $default:expr) => {
|
||||
$crate::Template::new({
|
||||
const ITEMS: &'static [$crate::Item<'static>] = &[$($item),*];
|
||||
ITEMS
|
||||
}, ::core::option::Option::Some($default))
|
||||
|
||||
($( $token:tt ),* $(,)?) => {
|
||||
$crate::__template_impl!($( $token ),* ; ::core::option::Option::None)
|
||||
};
|
||||
|
||||
($( $token:tt ),* $(,)? ; $default:expr) => {
|
||||
$crate::__template_impl!($( $token ),* ; ::core::option::Option::Some($default))
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{template, Item, Template};
|
||||
|
||||
#[test]
|
||||
fn test_template2() {
|
||||
assert_eq!(template!(), Template::new(&[], None),);
|
||||
|
||||
// Only literals
|
||||
assert_eq!(template!("1"), Template::new(&[Item::Text("1")], None));
|
||||
|
||||
assert_eq!(
|
||||
template!("1", "2"),
|
||||
Template::new(&[Item::Text("1"), Item::Text("2")], None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
template!("1", "2", "3"),
|
||||
Template::new(&[Item::Text("1"), Item::Text("2"), Item::Text("3")], None)
|
||||
);
|
||||
|
||||
// Only keys
|
||||
assert_eq!(template!({ "k1" }), Template::new(&[Item::Key("k1")], None));
|
||||
|
||||
assert_eq!(
|
||||
template!({ "k1" }, { "k2" }),
|
||||
Template::new(&[Item::Key("k1"), Item::Key("k2")], None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
template!({ "k1" }, { "k2" }, { "k3" }),
|
||||
Template::new(&[Item::Key("k1"), Item::Key("k2"), Item::Key("k3")], None)
|
||||
);
|
||||
|
||||
// Mixed
|
||||
assert_eq!(
|
||||
template!("1", { "k1" }, "3"),
|
||||
Template::new(&[Item::Text("1"), Item::Key("k1"), Item::Text("3")], None)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
template!("1", "2", { "k1" }, "3", "4"),
|
||||
Template::new(
|
||||
&[
|
||||
Item::Text("1"),
|
||||
Item::Text("2"),
|
||||
Item::Key("k1"),
|
||||
Item::Text("3"),
|
||||
Item::Text("4")
|
||||
],
|
||||
None
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
template!("1", "2", { "k1" }, { "k2" }, "3", "4", { "k3" }),
|
||||
Template::new(
|
||||
&[
|
||||
Item::Text("1"),
|
||||
Item::Text("2"),
|
||||
Item::Key("k1"),
|
||||
Item::Key("k2"),
|
||||
Item::Text("3"),
|
||||
Item::Text("4"),
|
||||
Item::Key("k3"),
|
||||
],
|
||||
None
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ impl<'s> Template<'s> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test_valid {
|
||||
use crate::{template, Item::*, Template};
|
||||
use crate::{template, Template};
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
|
@ -88,34 +88,31 @@ mod test_valid {
|
|||
#[test]
|
||||
fn no_keys() {
|
||||
let template = Template::parse("hello world").unwrap();
|
||||
assert_eq!(template, template!(Text("hello world")));
|
||||
assert_eq!(template, template!("hello world"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_key() {
|
||||
let template = Template::parse("{salutation} world").unwrap();
|
||||
assert_eq!(template, template!(Key("salutation"), Text(" world")));
|
||||
assert_eq!(template, template!({ "salutation" }, " world"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_key() {
|
||||
let template = Template::parse("hello {name}").unwrap();
|
||||
assert_eq!(template, template!(Text("hello "), Key("name")));
|
||||
assert_eq!(template, template!("hello ", { "name" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middle_key() {
|
||||
let template = Template::parse("hello {name}!").unwrap();
|
||||
assert_eq!(template, template!(Text("hello "), Key("name"), Text("!")));
|
||||
assert_eq!(template, template!("hello ", { "name" }, "!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middle_text() {
|
||||
let template = Template::parse("{salutation} good {title}").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(Key("salutation"), Text(" good "), Key("title"))
|
||||
);
|
||||
assert_eq!(template, template!({ "salutation" }, " good ", { "title" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -131,13 +128,13 @@ mod test_valid {
|
|||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
Text("\n And if thy native country was "),
|
||||
Key("ancient civilisation"),
|
||||
Text(",\n What need to slight thee? Came not "),
|
||||
Key("hero"),
|
||||
Text(" thence,\n Who gave to "),
|
||||
Key("country"),
|
||||
Text(" her books and art of writing?\n "),
|
||||
"\n And if thy native country was ",
|
||||
{ "ancient civilisation" },
|
||||
",\n What need to slight thee? Came not ",
|
||||
{ "hero" },
|
||||
" thence,\n Who gave to ",
|
||||
{ "country" },
|
||||
" her books and art of writing?\n ",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -145,19 +142,19 @@ mod test_valid {
|
|||
#[test]
|
||||
fn key_no_whitespace() {
|
||||
let template = Template::parse("{word}").unwrap();
|
||||
assert_eq!(template, template!(Key("word")));
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_leading_whitespace() {
|
||||
let template = Template::parse("{ word}").unwrap();
|
||||
assert_eq!(template, template!(Key("word")));
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_trailing_whitespace() {
|
||||
let template = Template::parse("{word\n}").unwrap();
|
||||
assert_eq!(template, template!(Key("word")));
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -168,46 +165,31 @@ mod test_valid {
|
|||
}",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(template, template!(Key("word")));
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_inner_whitespace() {
|
||||
let template = Template::parse("{ a word }").unwrap();
|
||||
assert_eq!(template, template!(Key("a word")));
|
||||
assert_eq!(template, template!({ "a word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_left() {
|
||||
let template = Template::parse(r"this \{ single left brace").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(Text("this "), Text("{"), Text(" single left brace"))
|
||||
);
|
||||
assert_eq!(template, template!("this ", "{", " single left brace"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_right() {
|
||||
let template = Template::parse(r"this \} single right brace").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(Text("this "), Text("}"), Text(" single right brace"))
|
||||
);
|
||||
assert_eq!(template, template!("this ", "}", " single right brace"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_both() {
|
||||
let template = Template::parse(r"these \{ two \} braces").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
Text("these "),
|
||||
Text("{"),
|
||||
Text(" two "),
|
||||
Text("}"),
|
||||
Text(" braces")
|
||||
)
|
||||
);
|
||||
assert_eq!(template, template!("these ", "{", " two ", "}", " braces"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -215,15 +197,7 @@ mod test_valid {
|
|||
let template = Template::parse(r"these \{\{ four \}\} braces").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
Text("these "),
|
||||
Text("{"),
|
||||
Text("{"),
|
||||
Text(" four "),
|
||||
Text("}"),
|
||||
Text("}"),
|
||||
Text(" braces")
|
||||
)
|
||||
template!("these ", "{", "{", " four ", "}", "}", " braces")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -232,13 +206,7 @@ mod test_valid {
|
|||
let template = Template::parse(r"these \\ backslashes \\\\").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
Text("these "),
|
||||
Text(r"\"),
|
||||
Text(" backslashes "),
|
||||
Text(r"\"),
|
||||
Text(r"\"),
|
||||
)
|
||||
template!("these ", r"\", " backslashes ", r"\", r"\",)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -247,16 +215,7 @@ mod test_valid {
|
|||
let template = Template::parse(r"\\{ a } \{{ b } \}{ c }").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
Text(r"\"),
|
||||
Key("a"),
|
||||
Text(" "),
|
||||
Text(r"{"),
|
||||
Key("b"),
|
||||
Text(" "),
|
||||
Text(r"}"),
|
||||
Key("c"),
|
||||
)
|
||||
template!(r"\", { "a" }, " ", r"{", { "b" }, " ", r"}", { "c" })
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -265,44 +224,32 @@ mod test_valid {
|
|||
let template = Template::parse(r"{ a }\\ { b }\{ { c }\}").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
Key("a"),
|
||||
Text(r"\"),
|
||||
Text(" "),
|
||||
Key("b"),
|
||||
Text(r"{"),
|
||||
Text(" "),
|
||||
Key("c"),
|
||||
Text(r"}"),
|
||||
)
|
||||
template!({ "a" }, r"\", " ", { "b" }, r"{", " ", { "c" }, r"}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_texts() {
|
||||
let template = Template::parse("幸徳 {particle} 秋水").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(Text("幸徳 "), Key("particle"), Text(" 秋水"))
|
||||
);
|
||||
assert_eq!(template, template!("幸徳 ", { "particle" }, " 秋水"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_key() {
|
||||
let template = Template::parse("The { 連盟 }").unwrap();
|
||||
assert_eq!(template, template!(Text("The "), Key("連盟")));
|
||||
assert_eq!(template, template!("The ", { "連盟" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_both() {
|
||||
let template = Template::parse("大杉 {栄}").unwrap();
|
||||
assert_eq!(template, template!(Text("大杉 "), Key("栄")));
|
||||
assert_eq!(template, template!("大杉 ", { "栄" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_whitespace() {
|
||||
let template = Template::parse("岩佐 作{ 太 }郎").unwrap();
|
||||
assert_eq!(template, template!(Text("岩佐 作"), Key("太"), Text("郎")));
|
||||
assert_eq!(template, template!("岩佐 作", { "太" }, "郎"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -310,26 +257,20 @@ mod test_valid {
|
|||
let template = Template::parse(r"日本\{アナキスト\}連盟").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
Text("日本"),
|
||||
Text(r"{"),
|
||||
Text("アナキスト"),
|
||||
Text(r"}"),
|
||||
Text("連盟")
|
||||
)
|
||||
template!("日本", r"{", "アナキスト", r"}", "連盟")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_rtl_text() {
|
||||
let template = Template::parse("محمد صايل").unwrap();
|
||||
assert_eq!(template, template!(Text("محمد صايل")));
|
||||
assert_eq!(template, template!("محمد صايل"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_rtl_key() {
|
||||
let template = Template::parse("محمد {ريشة}").unwrap();
|
||||
assert_eq!(template, template!(Text("محمد "), Key("ريشة")));
|
||||
assert_eq!(template, template!("محمد ", { "ريشة" }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{borrow::Cow, fmt::Display, io::Write, ops::Add};
|
||||
use std::{borrow::Cow, fmt::Display, io::Write, ops};
|
||||
|
||||
use crate::{ParseError, RenderError, Values};
|
||||
|
||||
|
@ -138,17 +138,20 @@ impl<'s> Template<'s> {
|
|||
Ok(String::from_utf8(buf).unwrap())
|
||||
}
|
||||
|
||||
/// If the template contains key `key`.
|
||||
pub fn has_key(&self, key: &str) -> bool {
|
||||
self.has_keys(&[key])
|
||||
self.has_any_of_keys(&[key])
|
||||
}
|
||||
|
||||
pub fn has_keys(&self, keys: &[&str]) -> bool {
|
||||
/// If the template contains any one of the `keys`.
|
||||
pub fn has_any_of_keys(&self, keys: &[&str]) -> bool {
|
||||
self.items.iter().any(|token| match token {
|
||||
Item::Key(k) => keys.contains(k),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns all keys in this template.
|
||||
pub fn keys(&self) -> impl Iterator<Item = &&str> {
|
||||
self.items.iter().filter_map(|token| match token {
|
||||
Item::Key(k) => Some(k),
|
||||
|
@ -160,39 +163,116 @@ impl<'s> Template<'s> {
|
|||
pub fn set_default(&mut self, default: &dyn Display) {
|
||||
self.default = Some(Cow::Owned(default.to_string()));
|
||||
}
|
||||
|
||||
/// Cast `Template<'s>` to `Template<'t>` where `'s` is a subtype of `'t`,
|
||||
/// meaning that `Template<'s>` outlives `Template<'t>`.
|
||||
pub fn cast<'t>(self) -> Template<'t>
|
||||
where
|
||||
's: 't,
|
||||
{
|
||||
Template {
|
||||
items: match self.items {
|
||||
Cow::Owned(vec) => Cow::Owned(vec),
|
||||
Cow::Borrowed(slice) => Cow::Borrowed(slice as &'t [Item<'t>]),
|
||||
},
|
||||
default: self.default.map(|default| default as Cow<'t, str>),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Add for Template<'s> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: Self) -> Self::Output {
|
||||
impl<'s, 'rhs: 's> ops::AddAssign<&Template<'rhs>> for Template<'s> {
|
||||
fn add_assign(&mut self, rhs: &Template<'rhs>) {
|
||||
self.items
|
||||
.to_mut()
|
||||
.extend(rhs.items.as_ref().iter().cloned());
|
||||
|
||||
if let Some(default) = &rhs.default {
|
||||
self.default = Some(default.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'rhs: 's> ops::AddAssign<Template<'rhs>> for Template<'s> {
|
||||
fn add_assign(&mut self, rhs: Template<'rhs>) {
|
||||
match rhs.items {
|
||||
Cow::Borrowed(items) => self.items.to_mut().extend(items.iter().cloned()),
|
||||
Cow::Owned(items) => self.items.to_mut().extend(items.into_iter()),
|
||||
}
|
||||
|
||||
if let Some(default) = rhs.default {
|
||||
self.default = Some(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'item: 's> ops::AddAssign<Item<'item>> for Template<'s> {
|
||||
fn add_assign(&mut self, item: Item<'item>) {
|
||||
self.items.to_mut().push(item);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'item: 's> ops::AddAssign<&Item<'item>> for Template<'s> {
|
||||
fn add_assign(&mut self, item: &Item<'item>) {
|
||||
self.add_assign(item.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'rhs: 's> ops::Add<Template<'rhs>> for Template<'s> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: Template<'rhs>) -> Self::Output {
|
||||
self += rhs;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'rhs: 's> ops::Add<&Template<'rhs>> for Template<'s> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: &Template<'rhs>) -> Self::Output {
|
||||
self += rhs;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'item: 's> ops::Add<Item<'item>> for Template<'s> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, item: Item<'item>) -> Self::Output {
|
||||
self += item;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'item: 's> ops::Add<&Item<'item>> for Template<'s> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, item: &Item<'item>) -> Self::Output {
|
||||
self += item;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::Item::{Key, Text};
|
||||
use crate::Template;
|
||||
|
||||
#[test]
|
||||
fn concat_templates() {
|
||||
let t1 = crate::template!(Text("Hello"), Key("name"));
|
||||
let t2 = crate::template!(Text("have a"), Key("adjective"), Text("day"));
|
||||
let t1 = crate::template!("Hello", { "name" });
|
||||
let t2 = crate::template!("have a", { "adjective" }, "day");
|
||||
assert_eq!(
|
||||
t1 + t2,
|
||||
crate::template!(
|
||||
Text("Hello"),
|
||||
Key("name"),
|
||||
Text("have a"),
|
||||
Key("adjective"),
|
||||
Text("day")
|
||||
),
|
||||
crate::template!("Hello", { "name" }, "have a", { "adjective" }, "day"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
fn inner<'a>(_: &'a u32, _: Template<'a>) {}
|
||||
|
||||
let template: Template<'static> = crate::template!("hello");
|
||||
let i = 1;
|
||||
inner(&i, template.cast());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@ use std::{
|
|||
};
|
||||
|
||||
pub trait Values {
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>>;
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>>;
|
||||
}
|
||||
|
||||
impl<T> Values for &T
|
||||
where
|
||||
T: Values,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
T::get_value(self, key)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ where
|
|||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.iter().find_map(|(k, v)| {
|
||||
if k.as_ref() == key {
|
||||
Some(Cow::Borrowed(v.as_ref()))
|
||||
|
@ -38,7 +38,7 @@ where
|
|||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
(*self).get_value(key)
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ where
|
|||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.as_slice().get_value(key)
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ where
|
|||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.as_slice().get_value(key)
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ where
|
|||
V: AsRef<str>,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.get(key).map(|v| Cow::Borrowed(v.as_ref()))
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ where
|
|||
K: Borrow<str> + Ord,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.get(key).map(|v| Cow::Borrowed(v.as_ref()))
|
||||
}
|
||||
}
|
||||
|
@ -87,25 +87,22 @@ where
|
|||
/// Workaround to allow using functions as [`Values`].
|
||||
///
|
||||
/// As this isn't constructible you'll want to use [`vals()`] instead.
|
||||
pub struct ValuesFn<F>
|
||||
where
|
||||
F: for<'s> Fn(&'s str) -> Option<Cow<'s, str>> + Send + 'static,
|
||||
{
|
||||
pub struct ValuesFn<F> {
|
||||
inner: F,
|
||||
}
|
||||
|
||||
impl<F> Values for ValuesFn<F>
|
||||
impl<'s, F> Values for &'s ValuesFn<F>
|
||||
where
|
||||
F: for<'s> Fn(&'s str) -> Option<Cow<'s, str>> + Send + 'static,
|
||||
F: Fn(&str) -> Option<Cow<'s, str>> + 's,
|
||||
{
|
||||
fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
(self.inner)(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> From<F> for ValuesFn<F>
|
||||
impl<'f, F> From<F> for ValuesFn<F>
|
||||
where
|
||||
F: for<'s> Fn(&'s str) -> Option<Cow<'s, str>> + Send + 'static,
|
||||
F: Fn(&str) -> Option<Cow<'f, str>> + 'f,
|
||||
{
|
||||
fn from(inner: F) -> Self {
|
||||
Self { inner }
|
||||
|
@ -123,11 +120,11 @@ where
|
|||
///
|
||||
/// fn use_values(_values: impl Values) {}
|
||||
///
|
||||
/// use_values(vals(|_| Some("hello".into())));
|
||||
/// use_values(&vals(|_| Some("hello".into())));
|
||||
/// ```
|
||||
pub const fn vals<F>(func: F) -> ValuesFn<F>
|
||||
pub const fn vals<'f, F>(func: F) -> ValuesFn<F>
|
||||
where
|
||||
F: for<'s> Fn(&'s str) -> Option<Cow<'s, str>> + Send + 'static,
|
||||
F: Fn(&str) -> Option<Cow<'f, str>> + 'f,
|
||||
{
|
||||
ValuesFn { inner: func }
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue