1个稳定版本
1.0.0 | 2022年1月6日 |
---|
#162 在 模板引擎 中
28 每月下载量
在 sbrd-gen 中使用
41KB
458 行
human-string-filler
一个用于人类友好的字符串替换的微小模板语言。
此crate旨在用于需要用户能够编写简单模板字符串并方便地评估它们的情况。它故意设计得很简单,以便在性能或功能方面没有惊喜,并且不会意外地绑定到Rust(例如,您可以在JavaScript驱动的Web应用程序中轻松实现它),如果包含诸如数字格式说明符之类的功能,则可能会发生这种情况——相反,如果您想要这种类型的功能,您将不得不自行实现它(别担心,这不会很难)。
此模板语言不提供任何逻辑,只有简单的字符串格式化:`{…}
` 模板区域将被替换成您决定的方式,花括号通过重复它们来转义(`{{
` 和 `}}
`),就是这样。
示例用法
最低级别的处理看起来像这样
use human_string_filler::{StrExt, SimpleFillerError};
let mut output = String::new();
"Hello, {name}!".fill_into(&mut output, |output: &mut String, key: &str| {
match key {
"name" => output.push_str("world"),
_ => return Err(SimpleFillerError::NoSuchKey),
}
Ok(())
}).unwrap();
assert_eq!(output, "Hello, world!");
template.fill_into(output, filler)
(由StrExt
提供)也可以写成fill(template, filler, output)
,如果您更喜欢函数而不是方法(我认为方法语法更清晰,但观点会不同,所以我提供了两种方法)。
填充函数为了在计算值的情况下提高效率,直接将内容追加到字符串中,并返回 Result<(), E>
;任何错误都将变成填充调用时的 Err(Error::BadReplacement { error, .. })
。在这个例子中我使用了 SimpleFillerError::NoSuchKey
,但是 ()
几乎也能达到相同的效果,或者你可以完全编写自己的错误类型。
这个例子展示了一个闭包,它接受 &mut String
并使用 .push_str(…)
,但这个crate并不以任何方式依赖于 String
:为了更大的通用性,你会使用一个泛型函数,该函数泛型化实现了 std::fmt::Write
的类型,并在内部使用 .write_str(…)?
(因为 SimpleFillerError
实现了 From<std::fmt::Error>
)。
在 更高层次 上,你可以使用字符串-字符串映射作为填充,你也可以直接使用 .fill_to_string()
(也可以作为独立的函数 fill_to_string
)填充到一个 String
。
use std::collections::HashMap;
use human_string_filler::StrExt;
let mut map = HashMap::new();
map.insert("name", "world");
let s = "Hello, {name}!".fill_to_string(&map);
assert_eq!(s.unwrap(), "Hello, world!");
或者如果你喜欢,可以为你自己的某些类型实现 Filler
特性。
Cargo 功能
-
std(默认启用):用于
#![no_std]
操作。隐含 alloc。- 为
std::error::Error
实现Error
; - 为
&HashMap
实现Filler
。
- 为
-
alloc(通过 std 默认启用)
- 为
&BTreeMap
实现Filler
。 fill_to_string
和StrExt::fill_to_string
。
- 为
模板语言
这是模板语言在 ABNF 中的语法。
unescaped-normal-char = %x00-7A / %x7C / %x7E-D7FF / %xE000-10FFFF
; any Unicode scalar value except for "{" and "}"
normal-char = unescaped-normal-char / "{{" / "}}"
template-region = "{" *unescaped-normal-char "}"
template-string = *( normal-char / template-region )
这个正则表达式将验证一个模板字符串。
^([^{}]|\{\{|\}\}|\{[^{}]*\})*$
样本合法模板字符串
- 空字符串
Hello, {name}!
:一个带有键 "name" 的模板区域。今天是 {date:short}
:一个带有“date:short”键的模板区域。尽管没有像format!()
宏那样的格式指定,但冒号约定是一种合理的选项——参见下一节。Hello, {}!
:一个带有空键的模板区域,不建议但允许。Escaped {{花括号 {和替换} for {fun}!
:字符串 "Escaped { 花括号 ",后面跟着一个带有键 "and replacements" 的模板区域,然后是字符串 " for ",然后是带有键 "fun" 的模板区域,最后是字符串 "!"。
示例非法模板字符串
hello, {world}foo}
:括号必须匹配;其他任何字符(特别是字符串的最后一个字符)必须通过重复来转义。{{thing}
:`{` 是转义的开括号,所以 `}` 是不匹配的。{thi{{n}}g}
:模板区域键中不能有任何形式的括号。(未来版本可能会使转义模板区域内的括号成为可能,如果它在像格式说明符这样的东西中证明是有用的;但不是现在。)
键语义约定
键是一个任意字符串(除了它不能包含 `{ 或 `}`),没有显式定义的语义,但这里有一些建议,包括辅助函数。
-
如果有格式指定符(例如,指定要使用的日期格式或是否用前导零填充数字,等等)是有意义的,则在像 `:` 这样的字符上进行一次分割。为此,提供了一个名为 `split_on 的函数。
-
对于更高级的格式化,其中您希望设置多个属性,`split_propertied 为诸如 `{key prop1 prop2=val2}` 和 `{key:prop1,prop2=val2}` 这样的字符串提供了一些合理且简单的语义。
-
如果嵌套属性访问是有意义的,则使用 `. 进行分割,使用 `key.split('.')` 迭代器。如果您正在使用上面提到的 `split_on 或 `split_propertied,您可能希望首先应用它们来分离键部分。
-
仅使用UAX #31标识符作为键(或键,如果支持嵌套属性访问)。大多数时候,空字符串和数字可能不是一个好主意。
根据这些建议,您最终可能会得到以下关键代码:foo.bar:baz
,这会被解释为从“foo”对象中检索“bar”属性,并按照“baz”进行格式化;或者 aleph.beth.gimmel|alpha beta=5
,这会被解释为从“aleph”的“beth”中检索“gimmel”,并使用属性“alpha”设置为true和“beta”设置为5进行格式化。这些事情实际上意味着什么取决于您自己决定。我肯定是一无所知。
作者
Chris Morgan (chris-morgan)是human-string-filler的作者和维护者。
许可
版权所有 © 2022 Chris Morgan
本项目根据三个不同的许可条款进行分发,供您选择
- Blue Oak Model License 1.0.0: https://blueoakcouncil.org/license/1.0.0
- MIT License: https://opensource.org/licenses/MIT
- Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE/2.0
如果您没有特定原因选择MIT或Apache-2.0许可,Chris Morgan建议您选择BlueOak-1.0.0,它比MIT和Apache-2.0更好、更简单,MIT和Apache-2.0只是由于它们更高的知名度以及在Rust生态系统中的传统使用而提供。(BlueOak-1.0.0仅在2019年3月发布。)
在使用此代码时,请确保您至少遵守这些许可条款之一。