mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-25 06:40:03 +00:00
Refactor: Mv leon and leon-macros into another repo (#1628)
* Refactor: Mv leon and leon-macros into another repo It's moved to https://github.com/cargo-bins/leon Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix CI: Rm `cargo-hack` check for `leon` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> --------- Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
2d29534754
commit
1928e2ccb3
33 changed files with 11 additions and 2786 deletions
|
@ -14,7 +14,7 @@ license = "GPL-3.0-only"
|
|||
atomic-file-install = { version = "1.0.1", path = "../atomic-file-install" }
|
||||
binstalk-types = { version = "0.7.0", path = "../binstalk-types" }
|
||||
compact_str = { version = "0.7.0", features = ["serde"] }
|
||||
leon = { version = "3.0.0", path = "../leon" }
|
||||
leon = "3.0.0"
|
||||
miette = "7.0.0"
|
||||
normalize-path = { version = "0.2.1", path = "../normalize-path" }
|
||||
thiserror = "1.0.52"
|
||||
|
|
|
@ -18,8 +18,8 @@ bytes = "1.4.0"
|
|||
compact_str = { version = "0.7.0" }
|
||||
either = "1.8.1"
|
||||
itertools = "0.12.0"
|
||||
leon = { version = "3.0.0", path = "../leon" }
|
||||
leon-macros = { version = "1.0.1", path = "../leon-macros" }
|
||||
leon = "3.0.0"
|
||||
leon-macros = "1.0.1"
|
||||
miette = "7.0.0"
|
||||
minisign-verify = "0.2.1"
|
||||
once_cell = "1.18.0"
|
||||
|
|
|
@ -17,7 +17,7 @@ binstalk-downloader = { version = "0.10.0", path = "../binstalk-downloader", def
|
|||
binstalk-types = { version = "0.7.0", path = "../binstalk-types" }
|
||||
cargo-toml-workspace = { version = "5.0.0", path = "../cargo-toml-workspace" }
|
||||
compact_str = { version = "0.7.0", features = ["serde"] }
|
||||
leon = { version = "3.0.0", path = "../leon" }
|
||||
leon = "3.0.0"
|
||||
miette = "7.0.0"
|
||||
normalize-path = { version = "0.2.1", path = "../normalize-path" }
|
||||
once_cell = "1.18.0"
|
||||
|
|
|
@ -22,7 +22,7 @@ detect-targets = { version = "0.1.15", path = "../detect-targets", features = ["
|
|||
either = "1.8.1"
|
||||
itertools = "0.12.0"
|
||||
jobslot = { version = "0.2.11", features = ["tokio"] }
|
||||
leon = { version = "3.0.0", path = "../leon" }
|
||||
leon = "3.0.0"
|
||||
maybe-owned = "0.3.4"
|
||||
miette = "7.0.0"
|
||||
semver = { version = "1.0.17", features = ["serde"] }
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
[package]
|
||||
name = "leon-macros"
|
||||
version = "1.0.1"
|
||||
edition = "2021"
|
||||
description = "Proc macros for crate leon"
|
||||
repository = "https://github.com/cargo-bins/cargo-binstall"
|
||||
documentation = "https://docs.rs/leon-macros"
|
||||
rust-version = "1.61.0"
|
||||
authors = ["Félix Saparelli <felix@passcod.name>", "Jiahao XU Jiahao_XU@outlook"]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
leon = { version = "3.0.0", path = "../leon", default-features = false }
|
||||
proc-macro2 = "1.0.68"
|
||||
syn = { version = "2.0.43", default-features = false, features = ["proc-macro", "parsing"] }
|
||||
quote = "1.0.28"
|
|
@ -1,176 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
|
@ -1,23 +0,0 @@
|
|||
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.
|
|
@ -1,36 +0,0 @@
|
|||
use leon::{Item, Template};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, LitStr};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn template(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as LitStr).value();
|
||||
|
||||
#[allow(clippy::unnecessary_to_owned)]
|
||||
let items = Template::parse(&input)
|
||||
.unwrap()
|
||||
.items
|
||||
.into_owned()
|
||||
.into_iter()
|
||||
.map(|item| match item {
|
||||
Item::Text(text) => quote! {
|
||||
::leon::Item::Text(#text)
|
||||
},
|
||||
Item::Key(key) => quote! {
|
||||
::leon::Item::Key(#key)
|
||||
},
|
||||
});
|
||||
|
||||
quote! {
|
||||
::leon::Template::new(
|
||||
{
|
||||
const ITEMS: &'static [::leon::Item<'static>] = &[
|
||||
#(#items),*
|
||||
];
|
||||
ITEMS
|
||||
},
|
||||
::core::option::Option::None,
|
||||
)
|
||||
}
|
||||
.into()
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
use leon::{Item, Template};
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
assert_eq!(leon_macros::template!(""), Template::new(&[], None),);
|
||||
|
||||
assert_eq!(
|
||||
leon_macros::template!("a"),
|
||||
Template::new(&[Item::Text("a")], None),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
leon_macros::template!("{1}"),
|
||||
Template::new(&[Item::Key("1")], None),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
leon_macros::template!("a{ 1 } c"),
|
||||
Template::new(&[Item::Text("a"), Item::Key("1"), Item::Text(" c")], None),
|
||||
);
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
[package]
|
||||
name = "leon"
|
||||
description = "Dead-simple string templating"
|
||||
repository = "https://github.com/cargo-bins/cargo-binstall"
|
||||
documentation = "https://docs.rs/leon"
|
||||
version = "3.0.0"
|
||||
rust-version = "1.61.0"
|
||||
authors = ["Félix Saparelli <felix@passcod.name>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
exclude = ["fuzz", "benches"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4.8", features = ["derive"], optional = true }
|
||||
miette = { version = "7.0.0", default-features = false, features = ["derive"], optional = true }
|
||||
thiserror = "1.0.52"
|
||||
|
||||
[features]
|
||||
default = ["miette"]
|
||||
cli = ["dep:clap", "miette", "miette?/fancy-no-backtrace"]
|
||||
miette = ["dep:miette"]
|
|
@ -1,176 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
|
@ -1,23 +0,0 @@
|
|||
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.
|
1
crates/leon/benches/.gitignore
vendored
1
crates/leon/benches/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
target
|
431
crates/leon/benches/Cargo.lock
generated
431
crates/leon/benches/Cargo.lock
generated
|
@ -1,431 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"atty",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "leon"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"miette",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leon-benches"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"leon",
|
||||
"serde",
|
||||
"tinytemplate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92a992891d5579caa9efd8e601f82e30a1caa79a27a5db075dde30ecb9eab357"
|
||||
dependencies = [
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "5.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c65c625186a9bcce6699394bee511e1b1aec689aa7e3be1bf4e996e75834153"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -1,30 +0,0 @@
|
|||
[package]
|
||||
name = "leon-benches"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leon = { path = ".." }
|
||||
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
tinytemplate = "1.2.1"
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bench]]
|
||||
name = "values"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "others"
|
||||
harness = false
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = "symbols"
|
|
@ -1,89 +0,0 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use leon::{vals, Template};
|
||||
use serde::Serialize;
|
||||
use tinytemplate::TinyTemplate;
|
||||
|
||||
fn compare_impls(c: &mut Criterion) {
|
||||
const TEMPLATE: &str = "hello {name}! i am {age} years old. my goal is to {goal}. i like: {flower}, {music}, {animal}, {color}, {food}. i'm drinking {drink}";
|
||||
fn replace_fn(key: &str) -> Option<Cow<'static, str>> {
|
||||
Some(Cow::Borrowed(match key {
|
||||
"name" => "marcus",
|
||||
"age" => "42",
|
||||
"goal" => "primary",
|
||||
"flower" => "lotus",
|
||||
"music" => "jazz",
|
||||
"animal" => "cat",
|
||||
"color" => "blue",
|
||||
"food" => "pizza",
|
||||
"drink" => "coffee",
|
||||
_ => return None,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Serialize)]
|
||||
struct Context<'c> {
|
||||
name: &'c str,
|
||||
age: u8,
|
||||
goal: &'c str,
|
||||
flower: &'c str,
|
||||
music: &'c str,
|
||||
animal: &'c str,
|
||||
color: &'c str,
|
||||
food: &'c str,
|
||||
drink: &'c str,
|
||||
}
|
||||
|
||||
let tt_context = Context {
|
||||
name: "marcus",
|
||||
age: 42,
|
||||
goal: "primary",
|
||||
flower: "lotus",
|
||||
music: "jazz",
|
||||
animal: "cat",
|
||||
color: "blue",
|
||||
food: "pizza",
|
||||
drink: "coffee",
|
||||
};
|
||||
|
||||
c.bench_function("leon", move |b| {
|
||||
b.iter(move || {
|
||||
let template = Template::parse(black_box(TEMPLATE)).unwrap();
|
||||
let output = template.render(&vals(replace_fn)).unwrap();
|
||||
black_box(output);
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("std, string replaces", move |b| {
|
||||
b.iter(move || {
|
||||
let mut output = black_box(TEMPLATE).to_string();
|
||||
for (key, value) in [
|
||||
("name", "marcus"),
|
||||
("age", "42"),
|
||||
("goal", "primary"),
|
||||
("flower", "lotus"),
|
||||
("music", "jazz"),
|
||||
("animal", "cat"),
|
||||
("color", "blue"),
|
||||
("food", "pizza"),
|
||||
("drink", "coffee"),
|
||||
] {
|
||||
output = output.replace(&format!("{{{}}}", key), value);
|
||||
}
|
||||
black_box(output);
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("tiny template", move |b| {
|
||||
b.iter(move || {
|
||||
let mut tt = TinyTemplate::new();
|
||||
tt.add_template("tmp", black_box(TEMPLATE)).unwrap();
|
||||
let output = tt.render("tmp", &tt_context).unwrap();
|
||||
black_box(output);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(compare, compare_impls);
|
||||
criterion_main!(compare);
|
|
@ -1,301 +0,0 @@
|
|||
use std::{borrow::Cow, collections::HashMap, sync::Arc};
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use leon::{vals, Template, Values, ValuesFn};
|
||||
|
||||
macro_rules! make_values {
|
||||
($($name:expr => $value:expr),*) => {
|
||||
(
|
||||
&[$(($name, $value)),*],
|
||||
{
|
||||
let mut map = HashMap::new();
|
||||
$(
|
||||
map.insert($name, $value);
|
||||
)*
|
||||
map
|
||||
},
|
||||
vals(|key| match key {
|
||||
$(
|
||||
$name => Some(Cow::Borrowed($value)),
|
||||
)*
|
||||
_ => None,
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
fn one_replace(c: &mut Criterion) {
|
||||
const TEMPLATE: &str = "Hello, {name}!";
|
||||
|
||||
let (slice, hashmap, vals) = make_values!(
|
||||
"name" => "marcus"
|
||||
);
|
||||
|
||||
inner_bench("one replace", c, TEMPLATE, vals, hashmap, slice);
|
||||
}
|
||||
|
||||
fn some_replaces(c: &mut Criterion) {
|
||||
const TEMPLATE: &str = "hello {name}! i am {age} years old. my goal is to {goal}. i like: {flower}, {music}, {animal}, {color}, {food}. i'm drinking {drink}";
|
||||
|
||||
let (slice, hashmap, vals) = make_values!(
|
||||
"name" => "marcus",
|
||||
"age" => "42",
|
||||
"goal" => "primary",
|
||||
"flower" => "lotus",
|
||||
"music" => "jazz",
|
||||
"animal" => "cat",
|
||||
"color" => "blue",
|
||||
"food" => "pizza",
|
||||
"drink" => "coffee"
|
||||
);
|
||||
|
||||
inner_bench("some replaces", c, TEMPLATE, vals, hashmap, slice);
|
||||
}
|
||||
|
||||
fn many_replaces(c: &mut Criterion) {
|
||||
const TEMPLATE: &str = "
|
||||
{artichoke}
|
||||
{aubergine}
|
||||
{asparagus}
|
||||
{broccoflower}
|
||||
{broccoli}
|
||||
{brussels sprouts}
|
||||
{cabbage}
|
||||
{kohlrabi}
|
||||
{Savoy cabbage}
|
||||
{red cabbage}
|
||||
{cauliflower}
|
||||
{celery}
|
||||
{endive}
|
||||
{fiddleheads}
|
||||
{frisee}
|
||||
{fennel}
|
||||
{greens}
|
||||
{arugula}
|
||||
{bok choy}
|
||||
{chard}
|
||||
{collard greens}
|
||||
{kale}
|
||||
{lettuce}
|
||||
{mustard greens}
|
||||
{spinach}
|
||||
{herbs}
|
||||
{anise}
|
||||
{basil}
|
||||
{caraway}
|
||||
{coriander}
|
||||
{chamomile}
|
||||
{daikon}
|
||||
{dill}
|
||||
{squash}
|
||||
{lavender}
|
||||
{cymbopogon}
|
||||
{marjoram}
|
||||
{oregano}
|
||||
{parsley}
|
||||
{rosemary}
|
||||
{thyme}
|
||||
{legumes}
|
||||
{alfalfa sprouts}
|
||||
{azuki beans}
|
||||
{bean sprouts}
|
||||
{black beans}
|
||||
{black-eyed peas}
|
||||
{borlotti bean}
|
||||
{broad beans}
|
||||
{chickpeas, garbanzos, or ceci beans}
|
||||
{green beans}
|
||||
{kidney beans}
|
||||
{lentils}
|
||||
{lima beans or butter bean}
|
||||
{mung beans}
|
||||
{navy beans}
|
||||
{peanuts}
|
||||
{pinto beans}
|
||||
{runner beans}
|
||||
{split peas}
|
||||
{soy beans}
|
||||
{peas}
|
||||
{mange tout or snap peas}
|
||||
{mushrooms}
|
||||
{nettles}
|
||||
{New Zealand spinach}
|
||||
{okra}
|
||||
{onions}
|
||||
{chives}
|
||||
{garlic}
|
||||
{leek}
|
||||
{onion}
|
||||
{shallot}
|
||||
{scallion}
|
||||
{peppers}
|
||||
{bell pepper}
|
||||
{chili pepper}
|
||||
{jalapeño}
|
||||
{habanero}
|
||||
{paprika}
|
||||
{tabasco pepper}
|
||||
{cayenne pepper}
|
||||
{radicchio}
|
||||
{rhubarb}
|
||||
{root vegetables}
|
||||
{beetroot}
|
||||
{beet}
|
||||
{mangelwurzel}
|
||||
{carrot}
|
||||
{celeriac}
|
||||
{corms}
|
||||
{eddoe}
|
||||
{konjac}
|
||||
{taro}
|
||||
{water chestnut}
|
||||
{ginger}
|
||||
{parsnip}
|
||||
{rutabaga}
|
||||
{radish}
|
||||
{wasabi}
|
||||
";
|
||||
|
||||
let (slice, hashmap, vals) = make_values!(
|
||||
"artichoke" => "Abiu",
|
||||
"aubergine" => "Açaí",
|
||||
"asparagus" => "Acerola",
|
||||
"broccoflower" => "Akebi",
|
||||
"broccoli" => "Ackee",
|
||||
"brussels sprouts" => "African Cherry Orange",
|
||||
"cabbage" => "American Mayapple",
|
||||
"kohlrabi" => "Apple",
|
||||
"Savoy cabbage" => "Apricot",
|
||||
"red cabbage" => "Araza",
|
||||
"cauliflower" => "Avocado",
|
||||
"celery" => "Banana",
|
||||
"endive" => "Bilberry",
|
||||
"fiddleheads" => "Blackberry",
|
||||
"frisee" => "Blackcurrant",
|
||||
"fennel" => "Black sapote",
|
||||
"greens" => "Blueberry",
|
||||
"arugula" => "Boysenberry",
|
||||
"bok choy" => "Breadfruit",
|
||||
"chard" => "Buddha's hand",
|
||||
"collard greens" => "Cactus pear",
|
||||
"kale" => "Canistel",
|
||||
"lettuce" => "Cashew",
|
||||
"mustard greens" => "Cempedak",
|
||||
"spinach" => "Cherimoya",
|
||||
"herbs" => "Cherry",
|
||||
"anise" => "Chico fruit",
|
||||
"basil" => "Cloudberry",
|
||||
"caraway" => "Coco de mer",
|
||||
"coriander" => "Coconut",
|
||||
"chamomile" => "Crab apple",
|
||||
"daikon" => "Cranberry",
|
||||
"dill" => "Currant",
|
||||
"squash" => "Damson",
|
||||
"lavender" => "Date",
|
||||
"cymbopogon" => "Dragonfruit",
|
||||
"marjoram" => "Pitaya",
|
||||
"oregano" => "Durian",
|
||||
"parsley" => "Elderberry",
|
||||
"rosemary" => "Feijoa",
|
||||
"thyme" => "Fig",
|
||||
"legumes" => "Finger Lime",
|
||||
"alfalfa sprouts" => "Caviar Lime",
|
||||
"azuki beans" => "Goji berry",
|
||||
"bean sprouts" => "Gooseberry",
|
||||
"black beans" => "Grape",
|
||||
"black-eyed peas" => "Raisin",
|
||||
"borlotti bean" => "Grapefruit",
|
||||
"broad beans" => "Grewia asiatica",
|
||||
"chickpeas, garbanzos, or ceci beans" => "Guava",
|
||||
"green beans" => "Hala Fruit",
|
||||
"kidney beans" => "Honeyberry",
|
||||
"lentils" => "Huckleberry",
|
||||
"lima beans or butter bean" => "Jabuticaba",
|
||||
"mung beans" => "Jackfruit",
|
||||
"navy beans" => "Jambul",
|
||||
"peanuts" => "Japanese plum",
|
||||
"pinto beans" => "Jostaberry",
|
||||
"runner beans" => "Jujube",
|
||||
"split peas" => "Juniper berry",
|
||||
"soy beans" => "Kaffir Lime",
|
||||
"peas" => "Kiwano",
|
||||
"mange tout or snap peas" => "Kiwifruit",
|
||||
"mushrooms" => "Kumquat",
|
||||
"nettles" => "Lemon",
|
||||
"New Zealand spinach" => "Lime",
|
||||
"okra" => "Loganberry",
|
||||
"onions" => "Longan",
|
||||
"chives" => "Loquat",
|
||||
"garlic" => "Lulo",
|
||||
"leek" => "Lychee",
|
||||
"onion" => "Magellan Barberry",
|
||||
"shallot" => "Mamey Apple",
|
||||
"scallion" => "Mamey Sapote",
|
||||
"peppers" => "Mango",
|
||||
"bell pepper" => "Mangosteen",
|
||||
"chili pepper" => "Marionberry",
|
||||
"jalapeño" => "Melon",
|
||||
"habanero" => "Cantaloupe",
|
||||
"paprika" => "Galia melon",
|
||||
"tabasco pepper" => "Honeydew",
|
||||
"cayenne pepper" => "Mouse melon",
|
||||
"radicchio" => "Musk melon",
|
||||
"rhubarb" => "Watermelon",
|
||||
"root vegetables" => "Miracle fruit",
|
||||
"beetroot" => "Momordica fruit",
|
||||
"beet" => "Monstera deliciosa",
|
||||
"mangelwurzel" => "Mulberry",
|
||||
"carrot" => "Nance",
|
||||
"celeriac" => "Nectarine",
|
||||
"corms" => "Orange",
|
||||
"eddoe" => "Blood orange",
|
||||
"konjac" => "Clementine",
|
||||
"taro" => "Mandarine",
|
||||
"water chestnut" => "Tangerine",
|
||||
"ginger" => "Papaya",
|
||||
"parsnip" => "Passionfruit",
|
||||
"rutabaga" => "Pawpaw",
|
||||
"radish" => "Peach",
|
||||
"wasabi" => "Pear"
|
||||
);
|
||||
|
||||
inner_bench("many replaces", c, TEMPLATE, vals, hashmap, slice);
|
||||
}
|
||||
|
||||
fn inner_bench<F>(
|
||||
name: &str,
|
||||
c: &mut Criterion,
|
||||
template_str: &str,
|
||||
vals: ValuesFn<F>,
|
||||
hashmap: HashMap<&str, &str>,
|
||||
slice: &[(&str, &str)],
|
||||
) where
|
||||
F: Fn(&str) -> Option<Cow<'static, str>> + Send + Clone + 'static,
|
||||
{
|
||||
c.bench_function(&format!("{name}, fn"), move |b| {
|
||||
let vals = vals.clone();
|
||||
b.iter(move || {
|
||||
let template = Template::parse(black_box(template_str)).unwrap();
|
||||
black_box(template.render(&vals).unwrap());
|
||||
})
|
||||
});
|
||||
let hashmap = Arc::new(hashmap);
|
||||
c.bench_function(&format!("{name}, hashmap"), move |b| {
|
||||
let hashmap = Arc::clone(&hashmap);
|
||||
b.iter(move || {
|
||||
let template = Template::parse(black_box(template_str)).unwrap();
|
||||
black_box(template.render(&hashmap).unwrap());
|
||||
})
|
||||
});
|
||||
c.bench_function(&format!("{name}, slice"), move |b| {
|
||||
b.iter(move || {
|
||||
let template = Template::parse(black_box(template_str)).unwrap();
|
||||
black_box(template.render(&slice as &dyn Values).unwrap());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(one, one_replace);
|
||||
criterion_group!(some, some_replaces);
|
||||
criterion_group!(many, many_replaces);
|
||||
criterion_main!(one, some, many);
|
4
crates/leon/fuzz/.gitignore
vendored
4
crates/leon/fuzz/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
|||
target
|
||||
corpus
|
||||
artifacts
|
||||
coverage
|
150
crates/leon/fuzz/Cargo.lock
generated
150
crates/leon/fuzz/Cargo.lock
generated
|
@ -1,150 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leon"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"miette",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leon-fuzz"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"leon",
|
||||
"libfuzzer-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4afd9b301defa984bbdbe112b4763e093ed191750a0d914a78c1106b2d0fe703"
|
||||
dependencies = [
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97c2401ab7ac5282ca5c8b518a87635b1a93762b0b90b9990c509888eeccba29"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
|
@ -1,27 +0,0 @@
|
|||
[package]
|
||||
name = "leon-fuzz"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
|
||||
[dependencies.leon]
|
||||
path = ".."
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
|
||||
[[bin]]
|
||||
name = "parser"
|
||||
path = "fuzz_targets/parser.rs"
|
||||
test = false
|
||||
doc = false
|
|
@ -1,9 +0,0 @@
|
|||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(s) = std::str::from_utf8(data) {
|
||||
let _ = leon::Template::parse(s);
|
||||
}
|
||||
});
|
|
@ -1,89 +0,0 @@
|
|||
use thiserror::Error as ThisError;
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
#[cfg_attr(feature = "miette", derive(miette::Diagnostic))]
|
||||
pub enum RenderError {
|
||||
/// A key was missing from the provided values.
|
||||
#[error("missing key `{0}`")]
|
||||
MissingKey(String),
|
||||
|
||||
/// An I/O error passed through from [`Template::render_into`](crate::Template::render_into).
|
||||
#[error("write failed: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// An error that can occur when parsing a template.
|
||||
///
|
||||
/// When the `miette` feature is enabled, this is a rich miette-powered error
|
||||
/// which will highlight the source of the error in the template when output
|
||||
/// (with miette's `fancy` feature). With `miette` disabled, this is opaque.
|
||||
#[derive(Clone, Debug, ThisError, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "miette", derive(miette::Diagnostic))]
|
||||
#[cfg_attr(feature = "miette", diagnostic(transparent))]
|
||||
#[error(transparent)]
|
||||
pub struct ParseError(Box<InnerParseError>);
|
||||
|
||||
/// The inner (unboxed) type of [`ParseError`].
|
||||
#[derive(Clone, Debug, ThisError, PartialEq, Eq)]
|
||||
#[error("{kind} at span start = {offset}, len = {len}: {src}")]
|
||||
struct InnerParseError {
|
||||
src: String,
|
||||
offset: usize,
|
||||
len: usize,
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
#[cfg(feature = "miette")]
|
||||
impl miette::Diagnostic for InnerParseError {
|
||||
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
|
||||
Some(&self.src)
|
||||
}
|
||||
|
||||
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
|
||||
Some(Box::new(std::iter::once_with(|| {
|
||||
miette::LabeledSpan::new(Some(self.kind.to_string()), self.offset, self.len)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, ThisError, PartialEq, Eq)]
|
||||
enum ErrorKind {
|
||||
#[error("This bracket is not opening or closing anything. Try removing it, or escaping it with a backslash.")]
|
||||
Unbalanced,
|
||||
|
||||
#[error("This escape is malformed.")]
|
||||
Escape,
|
||||
|
||||
#[error("A key cannot be empty.")]
|
||||
KeyEmpty,
|
||||
|
||||
#[error("Escapes are not allowed in keys.")]
|
||||
KeyEscape,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
fn new(src: &str, start: usize, end: usize, kind: ErrorKind) -> Self {
|
||||
Self(Box::new(InnerParseError {
|
||||
src: String::from(src),
|
||||
offset: start,
|
||||
len: end.saturating_sub(start) + 1,
|
||||
kind,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn unbalanced(src: &str, start: usize, end: usize) -> Self {
|
||||
Self::new(src, start, end, ErrorKind::Unbalanced)
|
||||
}
|
||||
|
||||
pub(crate) fn escape(src: &str, start: usize, end: usize) -> Self {
|
||||
Self::new(src, start, end, ErrorKind::Escape)
|
||||
}
|
||||
|
||||
pub(crate) fn key_empty(src: &str, start: usize, end: usize) -> Self {
|
||||
Self::new(src, start, end, ErrorKind::KeyEmpty)
|
||||
}
|
||||
|
||||
pub(crate) fn key_escape(src: &str, start: usize, end: usize) -> Self {
|
||||
Self::new(src, start, end, ErrorKind::KeyEscape)
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
//! Dead-simple string templating.
|
||||
//!
|
||||
//! Leon parses a template string into a list of tokens, and then substitutes
|
||||
//! provided values in. Unlike other templating engines, it is extremely simple:
|
||||
//! it supports no logic, only replaces. It is even simpler than `format!()`,
|
||||
//! albeit with a similar syntax.
|
||||
//!
|
||||
//! # Syntax
|
||||
//!
|
||||
//! ```plain
|
||||
//! it is better to rule { group }
|
||||
//! one can live {adverb} without power
|
||||
//! ```
|
||||
//!
|
||||
//! A replacement is denoted by `{` and `}`. The contents of the braces, trimmed
|
||||
//! of any whitespace, are the key. Any text outside of braces is left as-is.
|
||||
//!
|
||||
//! To escape a brace, use `\{` or `\}`. To escape a backslash, use `\\`. Keys
|
||||
//! cannot contain escapes.
|
||||
//!
|
||||
//! ```plain
|
||||
//! \{ leon \}
|
||||
//! ```
|
||||
//!
|
||||
//! The above examples, given the values `group = "no one"` and
|
||||
//! `adverb = "honourably"`, would render to:
|
||||
//!
|
||||
//! ```plain
|
||||
//! it is better to rule no one
|
||||
//! one can live honourably without power
|
||||
//! { leon }
|
||||
//! ```
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! A template is first parsed to a token list:
|
||||
//!
|
||||
//! ```
|
||||
//! use leon::Template;
|
||||
//!
|
||||
//! let template = Template::parse("hello {name}").unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! The template can be inspected, for example to check if a key is present:
|
||||
//!
|
||||
//! ```
|
||||
//! # use leon::Template;
|
||||
//! #
|
||||
//! # let template = Template::parse("hello {name}").unwrap();
|
||||
//! assert!(template.has_key("name"));
|
||||
//! ```
|
||||
//!
|
||||
//! The template can be rendered to a string:
|
||||
//!
|
||||
//! ```
|
||||
//! # use leon::Template;
|
||||
//! use leon::vals;
|
||||
//! #
|
||||
//! # let template = Template::parse("hello {name}").unwrap();
|
||||
//! assert_eq!(
|
||||
//! template.render(
|
||||
//! &&vals(|_key| Some("marcus".into()))
|
||||
//! ).unwrap().as_str(),
|
||||
//! "hello marcus",
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! …or to a writer:
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io::Write;
|
||||
//! # use leon::Template;
|
||||
//! use leon::vals;
|
||||
//! #
|
||||
//! # let template = Template::parse("hello {name}").unwrap();
|
||||
//! let mut buf: Vec<u8> = Vec::new();
|
||||
//! template.render_into(
|
||||
//! &mut buf,
|
||||
//! &&vals(|key| if key == "name" {
|
||||
//! Some("julius".into())
|
||||
//! } else {
|
||||
//! None
|
||||
//! })
|
||||
//! ).unwrap();
|
||||
//! assert_eq!(buf.as_slice(), b"hello julius");
|
||||
//! ```
|
||||
//!
|
||||
//! …with a map:
|
||||
//!
|
||||
//! ```
|
||||
//! use std::collections::HashMap;
|
||||
//! # use leon::Template;
|
||||
//! # let template = Template::parse("hello {name}").unwrap();
|
||||
//! let mut values = HashMap::new();
|
||||
//! values.insert("name", "brutus");
|
||||
//! assert_eq!(template.render(&values).unwrap().as_str(), "hello brutus");
|
||||
//! ```
|
||||
//!
|
||||
//! …or with your own type, if you implement the [`Values`] trait:
|
||||
//!
|
||||
//! ```
|
||||
//! # use leon::Template;
|
||||
//! use std::borrow::Cow;
|
||||
//! use leon::Values;
|
||||
//!
|
||||
//! struct MyMap {
|
||||
//! name: &'static str,
|
||||
//! }
|
||||
//! impl Values for MyMap {
|
||||
//! fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
//! if key == "name" {
|
||||
//! Some(self.name.into())
|
||||
//! } else {
|
||||
//! None
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! #
|
||||
//! # let template = Template::parse("hello {name}").unwrap();
|
||||
//! let values = MyMap { name: "pontifex" };
|
||||
//! assert_eq!(template.render(&values).unwrap().as_str(), "hello pontifex");
|
||||
//! ```
|
||||
//!
|
||||
//! # Compile-time parsing
|
||||
//!
|
||||
//! You can either use [`leon-macros`](https://docs.rs/leon-macros)'s
|
||||
//! [`template!`](https://docs.rs/leon-macros/latest/leon_macros/macro.template.html),
|
||||
//! a proc-macro, with the exact same syntax as the normal parser, or this
|
||||
//! crate's [`template!`] rules-macro, which requires a slightly different
|
||||
//! syntax but doesn't bring in additional dependencies. In either case,
|
||||
//! the leon library is required as a runtime dependency.
|
||||
//!
|
||||
//! # Errors
|
||||
//!
|
||||
//! Leon will return a [`ParseError`] if the template fails to
|
||||
//! parse. This can happen if there are unbalanced braces, or if a key is empty.
|
||||
//!
|
||||
//! Leon will return a [`RenderError::MissingKey`] if a key is missing from keyed
|
||||
//! values passed to [`Template::render()`], unless a default value is provided
|
||||
//! with [`Template.default`].
|
||||
//!
|
||||
//! It will also pass through I/O errors when using [`Template::render_into()`].
|
||||
|
||||
#[doc(inline)]
|
||||
pub use error::*;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use template::*;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use values::*;
|
||||
|
||||
mod error;
|
||||
mod macros;
|
||||
mod parser;
|
||||
mod template;
|
||||
mod values;
|
|
@ -1,156 +0,0 @@
|
|||
#[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`](crate::Template::parse).
|
||||
///
|
||||
/// This is essentially a shorthand for:
|
||||
///
|
||||
/// ```
|
||||
/// use leon::{Item, Template};
|
||||
/// Template::new({
|
||||
/// const ITEMS: &'static [Item<'static>] = &[Item::Text("Hello "), Item::Key("name")];
|
||||
/// ITEMS
|
||||
/// }, Some("world"));
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// assert_eq!(
|
||||
/// leon::template!("Hello ", {"name"})
|
||||
/// .render(&[("name", "Магда Нахман")])
|
||||
/// .unwrap(),
|
||||
/// "Hello Магда Нахман",
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// With a default:
|
||||
///
|
||||
/// ```
|
||||
/// assert_eq!(
|
||||
/// leon::template!("Hello ", {"name"}; "M. P. T. Acharya")
|
||||
/// .render(&[("city", "Madras")])
|
||||
/// .unwrap(),
|
||||
/// "Hello M. P. T. Acharya",
|
||||
/// );
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! template {
|
||||
() => {
|
||||
$crate::Template::new(
|
||||
{
|
||||
const ITEMS: &'static [$crate::Item<'static>] = &[];
|
||||
ITEMS
|
||||
},
|
||||
::core::option::Option::None,
|
||||
)
|
||||
};
|
||||
|
||||
($( $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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
#[cfg(feature = "cli")]
|
||||
fn main() -> miette::Result<()> {
|
||||
use std::{collections::HashMap, error::Error, io::stdout};
|
||||
|
||||
use clap::Parser;
|
||||
use leon::Template;
|
||||
|
||||
/// Render a Leon template with the given values.
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Leon template
|
||||
template: String,
|
||||
|
||||
/// Default to use for missing keys
|
||||
#[arg(long)]
|
||||
default: Option<String>,
|
||||
|
||||
/// Use values from the environment
|
||||
#[arg(long)]
|
||||
env: bool,
|
||||
|
||||
/// Key-value pairs to use
|
||||
#[arg(short, long, value_parser = parse_key_val::<String, String>)]
|
||||
values: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
/// Parse a single key-value pair
|
||||
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
T::Err: Error + Send + Sync + 'static,
|
||||
U: std::str::FromStr,
|
||||
U::Err: Error + Send + Sync + 'static,
|
||||
{
|
||||
let (k, v) = s
|
||||
.split_once('=')
|
||||
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
|
||||
Ok((k.parse()?, v.parse()?))
|
||||
}
|
||||
|
||||
let args = Args::parse();
|
||||
let mut values: HashMap<String, String> = HashMap::from_iter(args.values);
|
||||
if args.env {
|
||||
for (key, value) in std::env::vars() {
|
||||
values.entry(key).or_insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
let template = args.template;
|
||||
let mut template = Template::parse(&template)?;
|
||||
if let Some(default) = &args.default {
|
||||
template.set_default(default);
|
||||
}
|
||||
|
||||
template.render_into(&mut stdout().lock(), &values)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cli"))]
|
||||
fn main() {}
|
|
@ -1,334 +0,0 @@
|
|||
use crate::{Item, ParseError, Template};
|
||||
|
||||
impl<'s> Template<'s> {
|
||||
pub(crate) fn parse_items(source: &'s str) -> Result<Vec<Item<'s>>, ParseError> {
|
||||
let mut items = Vec::new();
|
||||
|
||||
let mut start = 0;
|
||||
let mut s = source;
|
||||
|
||||
loop {
|
||||
if let Some(index) = s.find(['\\', '{', '}']) {
|
||||
if index != 0 {
|
||||
let (first, last) = s.split_at(index);
|
||||
items.push(Item::Text(first));
|
||||
|
||||
// Move cursor forward
|
||||
start += index;
|
||||
s = last;
|
||||
}
|
||||
} else {
|
||||
if !s.is_empty() {
|
||||
items.push(Item::Text(s));
|
||||
}
|
||||
|
||||
break Ok(items);
|
||||
};
|
||||
|
||||
let mut chars = s.chars();
|
||||
let ch = chars.next().unwrap();
|
||||
|
||||
match ch {
|
||||
'\\' => {
|
||||
match chars.next() {
|
||||
Some('\\' | '{' | '}') => {
|
||||
let t = s.get(1..2).unwrap();
|
||||
debug_assert!(["\\", "{", "}"].contains(&t), "{}", t);
|
||||
items.push(Item::Text(t));
|
||||
|
||||
// Move cursor forward
|
||||
start += 2;
|
||||
s = s.get(2..).unwrap();
|
||||
}
|
||||
_ => {
|
||||
return Err(ParseError::escape(source, start, start + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
'{' => {
|
||||
let Some((key, rest)) = s[1..].split_once('}') else {
|
||||
return Err(ParseError::unbalanced(source, start, start + s.len()));
|
||||
};
|
||||
if let Some(index) = key.find('\\') {
|
||||
return Err(ParseError::key_escape(source, start, start + 1 + index));
|
||||
}
|
||||
|
||||
let k = key.trim();
|
||||
if k.is_empty() {
|
||||
return Err(ParseError::key_empty(source, start, start + key.len() + 1));
|
||||
}
|
||||
items.push(Item::Key(k));
|
||||
|
||||
// Move cursor forward
|
||||
// for the '{'
|
||||
// | for the '}'
|
||||
// | |
|
||||
start += 1 + key.len() + 1;
|
||||
s = rest;
|
||||
}
|
||||
'}' => {
|
||||
return Err(ParseError::unbalanced(source, start, start));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_valid {
|
||||
use crate::{template, Template};
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let template = Template::parse("").unwrap();
|
||||
assert_eq!(template, Template::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_keys() {
|
||||
let template = Template::parse("hello world").unwrap();
|
||||
assert_eq!(template, template!("hello world"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_key() {
|
||||
let template = Template::parse("{salutation} world").unwrap();
|
||||
assert_eq!(template, template!({ "salutation" }, " world"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_key() {
|
||||
let template = Template::parse("hello {name}").unwrap();
|
||||
assert_eq!(template, template!("hello ", { "name" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middle_key() {
|
||||
let template = Template::parse("hello {name}!").unwrap();
|
||||
assert_eq!(template, template!("hello ", { "name" }, "!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middle_text() {
|
||||
let template = Template::parse("{salutation} good {title}").unwrap();
|
||||
assert_eq!(template, template!({ "salutation" }, " good ", { "title" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline() {
|
||||
let template = Template::parse(
|
||||
"
|
||||
And if thy native country was { ancient civilisation },
|
||||
What need to slight thee? Came not {hero} thence,
|
||||
Who gave to { country } her books and art of writing?
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(
|
||||
"\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 ",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_no_whitespace() {
|
||||
let template = Template::parse("{word}").unwrap();
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_leading_whitespace() {
|
||||
let template = Template::parse("{ word}").unwrap();
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_trailing_whitespace() {
|
||||
let template = Template::parse("{word\n}").unwrap();
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_both_whitespace() {
|
||||
let template = Template::parse(
|
||||
"{
|
||||
\tword
|
||||
}",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(template, template!({ "word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_inner_whitespace() {
|
||||
let template = Template::parse("{ a word }").unwrap();
|
||||
assert_eq!(template, template!({ "a word" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_left() {
|
||||
let template = Template::parse(r"this \{ single left brace").unwrap();
|
||||
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!("this ", "}", " single right brace"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_both() {
|
||||
let template = Template::parse(r"these \{ two \} braces").unwrap();
|
||||
assert_eq!(template, template!("these ", "{", " two ", "}", " braces"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_doubled() {
|
||||
let template = Template::parse(r"these \{\{ four \}\} braces").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!("these ", "{", "{", " four ", "}", "}", " braces")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_escape() {
|
||||
let template = Template::parse(r"these \\ backslashes \\\\").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!("these ", r"\", " backslashes ", r"\", r"\",)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_before_key() {
|
||||
let template = Template::parse(r"\\{ a } \{{ b } \}{ c }").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!(r"\", { "a" }, " ", r"{", { "b" }, " ", r"}", { "c" })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_after_key() {
|
||||
let template = Template::parse(r"{ a }\\ { b }\{ { c }\}").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!({ "a" }, r"\", " ", { "b" }, r"{", " ", { "c" }, r"}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_texts() {
|
||||
let template = Template::parse("幸徳 {particle} 秋水").unwrap();
|
||||
assert_eq!(template, template!("幸徳 ", { "particle" }, " 秋水"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_key() {
|
||||
let template = Template::parse("The { 連盟 }").unwrap();
|
||||
assert_eq!(template, template!("The ", { "連盟" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_both() {
|
||||
let template = Template::parse("大杉 {栄}").unwrap();
|
||||
assert_eq!(template, template!("大杉 ", { "栄" }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_whitespace() {
|
||||
let template = Template::parse("岩佐 作{ 太 }郎").unwrap();
|
||||
assert_eq!(template, template!("岩佐 作", { "太" }, "郎"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_with_escapes() {
|
||||
let template = Template::parse(r"日本\{アナキスト\}連盟").unwrap();
|
||||
assert_eq!(
|
||||
template,
|
||||
template!("日本", r"{", "アナキスト", r"}", "連盟")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_rtl_text() {
|
||||
let template = Template::parse("محمد صايل").unwrap();
|
||||
assert_eq!(template, template!("محمد صايل"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multibyte_rtl_key() {
|
||||
let template = Template::parse("محمد {ريشة}").unwrap();
|
||||
assert_eq!(template, template!("محمد ", { "ريشة" }));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_error {
|
||||
use crate::{ParseError, Template};
|
||||
|
||||
#[test]
|
||||
fn key_left_half() {
|
||||
let template = Template::parse("{ open").unwrap_err();
|
||||
assert_eq!(template, ParseError::unbalanced("{ open", 0, 6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_right_half() {
|
||||
let template = Template::parse("open }").unwrap_err();
|
||||
assert_eq!(template, ParseError::unbalanced("open }", 5, 5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_with_half_escape() {
|
||||
let template = Template::parse(r"this is { not \ allowed }").unwrap_err();
|
||||
assert_eq!(
|
||||
template,
|
||||
ParseError::key_escape(r"this is { not \ allowed }", 8, 14)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_with_full_escape() {
|
||||
let template = Template::parse(r"{ not \} allowed }").unwrap_err();
|
||||
assert_eq!(
|
||||
template,
|
||||
ParseError::key_escape(r"{ not \} allowed }", 0, 6)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_empty() {
|
||||
let template = Template::parse(r"void: {}").unwrap_err();
|
||||
assert_eq!(template, ParseError::key_empty(r"void: {}", 6, 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_only_whitespace() {
|
||||
let template = Template::parse(r"nothing: { }").unwrap_err();
|
||||
assert_eq!(template, ParseError::key_empty(r"nothing: { }", 9, 11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_escape() {
|
||||
let template = Template::parse(r"not \a thing").unwrap_err();
|
||||
assert_eq!(template, ParseError::escape(r"not \a thing", 4, 5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end_escape() {
|
||||
let template = Template::parse(r"forget me not \").unwrap_err();
|
||||
assert_eq!(template, ParseError::escape(r"forget me not \", 14, 15));
|
||||
}
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
use std::{borrow::Cow, fmt::Display, io::Write, ops};
|
||||
|
||||
use crate::{ParseError, RenderError, Values};
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Template<'s> {
|
||||
pub items: Cow<'s, [Item<'s>]>,
|
||||
pub default: Option<Cow<'s, str>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Item<'s> {
|
||||
Text(&'s str),
|
||||
Key(&'s str),
|
||||
}
|
||||
|
||||
impl<'s> Template<'s> {
|
||||
/// Construct a template with the given items and default.
|
||||
///
|
||||
/// You can write a template literal without any help by constructing it directly:
|
||||
///
|
||||
/// ```
|
||||
/// use std::borrow::Cow;
|
||||
/// use leon::{Item, Template};
|
||||
/// const TEMPLATE: Template = Template {
|
||||
/// items: Cow::Borrowed({
|
||||
/// const ITEMS: &'static [Item<'static>] = &[
|
||||
/// Item::Text("Hello"),
|
||||
/// Item::Key("name"),
|
||||
/// ];
|
||||
/// ITEMS
|
||||
/// }),
|
||||
/// default: None,
|
||||
/// };
|
||||
/// assert_eq!(TEMPLATE.render(&[("name", "world")]).unwrap(), "Helloworld");
|
||||
/// ```
|
||||
///
|
||||
/// As that's a bit verbose, using this function and the enum shorthands can be helpful:
|
||||
///
|
||||
/// ```
|
||||
/// use leon::{Item, Item::*, Template};
|
||||
/// const TEMPLATE: Template = Template::new({
|
||||
/// const ITEMS: &'static [Item<'static>] = &[Text("Hello "), Key("name")];
|
||||
/// ITEMS
|
||||
/// }, Some("world"));
|
||||
///
|
||||
/// assert_eq!(TEMPLATE.render(&[("unrelated", "value")]).unwrap(), "Hello world");
|
||||
/// ```
|
||||
///
|
||||
/// For an even more ergonomic syntax, see the [`leon::template!`](crate::template!) macro.
|
||||
pub const fn new(items: &'s [Item<'s>], default: Option<&'s str>) -> Template<'s> {
|
||||
Template {
|
||||
items: Cow::Borrowed(items),
|
||||
default: match default {
|
||||
Some(default) => Some(Cow::Borrowed(default)),
|
||||
None => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a template from a string.
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// ```plain
|
||||
/// it is better to rule { group }
|
||||
/// one can live {adverb} without power
|
||||
/// ```
|
||||
///
|
||||
/// A replacement is denoted by `{` and `}`. The contents of the braces, trimmed
|
||||
/// of any whitespace, are the key. Any text outside of braces is left as-is.
|
||||
///
|
||||
/// To escape a brace, use `\{` or `\}`. To escape a backslash, use `\\`. Keys
|
||||
/// cannot contain escapes.
|
||||
///
|
||||
/// ```plain
|
||||
/// \{ leon \}
|
||||
/// ```
|
||||
///
|
||||
/// The above examples, given the values `group = "no one"` and
|
||||
/// `adverb = "honourably"`, would render to:
|
||||
///
|
||||
/// ```plain
|
||||
/// it is better to rule no one
|
||||
/// one can live honourably without power
|
||||
/// { leon }
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use leon::Template;
|
||||
/// let template = Template::parse("hello {name}").unwrap();
|
||||
/// ```
|
||||
///
|
||||
pub fn parse(s: &'s str) -> Result<Self, ParseError> {
|
||||
Self::parse_items(s).map(|items| Template {
|
||||
items: Cow::Owned(items),
|
||||
default: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render_into(
|
||||
&self,
|
||||
writer: &mut dyn Write,
|
||||
values: &dyn Values,
|
||||
) -> Result<(), RenderError> {
|
||||
for token in self.items.as_ref() {
|
||||
match token {
|
||||
Item::Text(text) => writer.write_all(text.as_bytes())?,
|
||||
Item::Key(key) => {
|
||||
if let Some(value) = values.get_value(key) {
|
||||
writer.write_all(value.as_bytes())?;
|
||||
} else if let Some(default) = &self.default {
|
||||
writer.write_all(default.as_bytes())?;
|
||||
} else {
|
||||
return Err(RenderError::MissingKey(key.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render(&self, values: &dyn Values) -> Result<String, RenderError> {
|
||||
let mut buf = Vec::with_capacity(
|
||||
self.items
|
||||
.iter()
|
||||
.map(|item| match item {
|
||||
Item::Key(_) => 0,
|
||||
Item::Text(t) => t.len(),
|
||||
})
|
||||
.sum(),
|
||||
);
|
||||
self.render_into(&mut buf, values)?;
|
||||
|
||||
// UNWRAP: We know that the buffer is valid UTF-8 because we only write strings.
|
||||
Ok(String::from_utf8(buf).unwrap())
|
||||
}
|
||||
|
||||
/// If the template contains key `key`.
|
||||
pub fn has_key(&self, key: &str) -> bool {
|
||||
self.has_any_of_keys(&[key])
|
||||
}
|
||||
|
||||
/// 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),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the default value for this template.
|
||||
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, '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),
|
||||
}
|
||||
|
||||
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::Template;
|
||||
|
||||
#[test]
|
||||
fn concat_templates() {
|
||||
let t1 = crate::template!("Hello", { "name" });
|
||||
let t2 = crate::template!("have a", { "adjective" }, "day");
|
||||
assert_eq!(
|
||||
t1 + t2,
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
collections::{BTreeMap, HashMap},
|
||||
hash::{BuildHasher, Hash},
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub trait Values {
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>>;
|
||||
}
|
||||
|
||||
impl<T> Values for &T
|
||||
where
|
||||
T: Values,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
T::get_value(self, key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Values for Arc<T>
|
||||
where
|
||||
T: Values,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
T::get_value(self.deref(), key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Values for Rc<T>
|
||||
where
|
||||
T: Values,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
T::get_value(self.deref(), key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Values for [(K, V)]
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<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()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Values for &[(K, V)]
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
(*self).get_value(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, const N: usize> Values for [(K, V); N]
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.as_slice().get_value(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Values for Vec<(K, V)>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.as_slice().get_value(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> Values for HashMap<K, V, S>
|
||||
where
|
||||
K: Borrow<str> + Eq + Hash,
|
||||
V: AsRef<str>,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.get(key).map(|v| Cow::Borrowed(v.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Values for BTreeMap<K, V>
|
||||
where
|
||||
K: Borrow<str> + Ord,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
self.get(key).map(|v| Cow::Borrowed(v.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Workaround to allow using functions as [`Values`].
|
||||
///
|
||||
/// As this isn't constructible you'll want to use [`vals()`] instead.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ValuesFn<F> {
|
||||
inner: F,
|
||||
}
|
||||
|
||||
impl<F> Values for ValuesFn<F>
|
||||
where
|
||||
F: Fn(&str) -> Option<Cow<'static, str>>,
|
||||
{
|
||||
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
|
||||
(self.inner)(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// See doc of [`vals`]
|
||||
impl<F> From<F> for ValuesFn<F>
|
||||
where
|
||||
F: Fn(&str) -> Option<Cow<'static, str>>,
|
||||
{
|
||||
fn from(inner: F) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps your function so it implements [`Values`],
|
||||
/// though it only works if your function returns `Cow<'static, str>`.
|
||||
///
|
||||
/// Since regular function pointers cannot return anything other than
|
||||
/// `Cow<'static, str>` and closure in Rust currently does not support
|
||||
/// returning borrows of captured data, supporting anything other than
|
||||
/// `Cow<'static, str>` for functions is pointless and would only cause
|
||||
/// more confusion and compile-time errors.
|
||||
///
|
||||
/// To return `&str` owned by the values itself, please create a newtype
|
||||
/// and implement [`Values`] on it manually instead of using this function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use leon::{Values, vals};
|
||||
///
|
||||
/// fn use_values(_values: impl Values) {}
|
||||
///
|
||||
/// use_values(&vals(|_| Some("hello".into())));
|
||||
/// ```
|
||||
pub const fn vals<F>(func: F) -> ValuesFn<F>
|
||||
where
|
||||
F: Fn(&str) -> Option<Cow<'static, str>>,
|
||||
{
|
||||
ValuesFn { inner: func }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue