aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared/src
diff options
context:
space:
mode:
authorLibravatar René Kijewski <kijewski@library.vetmed.fu-berlin.de>2022-06-08 12:52:00 +0200
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2022-06-08 13:36:48 +0200
commit6cce13f076bdd3e8d9b0bf888c0464a1af9df25f (patch)
tree7f54a53f844c5ff622c2af22437851010ad65b29 /askama_shared/src
parent0de54192a039943a687d579e15cb87bfc4801589 (diff)
downloadaskama-6cce13f076bdd3e8d9b0bf888c0464a1af9df25f.tar.gz
askama-6cce13f076bdd3e8d9b0bf888c0464a1af9df25f.tar.bz2
askama-6cce13f076bdd3e8d9b0bf888c0464a1af9df25f.zip
Merge askama_derive into askama
Diffstat (limited to 'askama_shared/src')
-rw-r--r--askama_shared/src/error.rs95
-rw-r--r--askama_shared/src/filters/json.rs44
-rw-r--r--askama_shared/src/filters/mod.rs636
-rw-r--r--askama_shared/src/filters/yaml.rs34
-rw-r--r--askama_shared/src/helpers/mod.rs48
-rw-r--r--askama_shared/src/lib.rs147
6 files changed, 0 insertions, 1004 deletions
diff --git a/askama_shared/src/error.rs b/askama_shared/src/error.rs
deleted file mode 100644
index 7c959a6..0000000
--- a/askama_shared/src/error.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use std::fmt::{self, Display};
-
-pub type Result<I, E = Error> = ::std::result::Result<I, E>;
-
-/// askama error type
-///
-/// # Feature Interaction
-///
-/// If the feature `serde_json` is enabled an
-/// additional error variant `Json` is added.
-///
-/// # Why not `failure`/`error-chain`?
-///
-/// Error from `error-chain` are not `Sync` which
-/// can lead to problems e.g. when this is used
-/// by a crate which use `failure`. Implementing
-/// `Fail` on the other hand prevents the implementation
-/// of `std::error::Error` until specialization lands
-/// on stable. While errors impl. `Fail` can be
-/// converted to a type impl. `std::error::Error`
-/// using a adapter the benefits `failure` would
-/// bring to this crate are small, which is why
-/// `std::error::Error` was used.
-///
-#[non_exhaustive]
-#[derive(Debug)]
-pub enum Error {
- /// formatting error
- Fmt(fmt::Error),
-
- /// an error raised by using `?` in a template
- Custom(Box<dyn std::error::Error + Send + Sync>),
-
- /// json conversion error
- #[cfg(feature = "serde_json")]
- Json(::serde_json::Error),
-
- /// yaml conversion error
- #[cfg(feature = "serde_yaml")]
- Yaml(::serde_yaml::Error),
-}
-
-impl std::error::Error for Error {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match *self {
- Error::Fmt(ref err) => Some(err),
- Error::Custom(ref err) => Some(err.as_ref()),
- #[cfg(feature = "serde_json")]
- Error::Json(ref err) => Some(err),
- #[cfg(feature = "serde_yaml")]
- Error::Yaml(ref err) => Some(err),
- }
- }
-}
-
-impl Display for Error {
- fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Error::Fmt(err) => write!(formatter, "formatting error: {}", err),
- Error::Custom(err) => write!(formatter, "{}", err),
- #[cfg(feature = "serde_json")]
- Error::Json(err) => write!(formatter, "json conversion error: {}", err),
- #[cfg(feature = "serde_yaml")]
- Error::Yaml(err) => write!(formatter, "yaml conversion error: {}", err),
- }
- }
-}
-
-impl From<fmt::Error> for Error {
- fn from(err: fmt::Error) -> Self {
- Error::Fmt(err)
- }
-}
-
-#[cfg(feature = "serde_json")]
-impl From<::serde_json::Error> for Error {
- fn from(err: ::serde_json::Error) -> Self {
- Error::Json(err)
- }
-}
-
-#[cfg(feature = "serde_yaml")]
-impl From<::serde_yaml::Error> for Error {
- fn from(err: ::serde_yaml::Error) -> Self {
- Error::Yaml(err)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::Error;
-
- trait AssertSendSyncStatic: Send + Sync + 'static {}
- impl AssertSendSyncStatic for Error {}
-}
diff --git a/askama_shared/src/filters/json.rs b/askama_shared/src/filters/json.rs
deleted file mode 100644
index e94e50c..0000000
--- a/askama_shared/src/filters/json.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use crate::error::{Error, Result};
-use askama_escape::JsonEscapeBuffer;
-use serde::Serialize;
-use serde_json::to_writer_pretty;
-
-/// Serialize to JSON (requires `json` feature)
-///
-/// The generated string does not contain ampersands `&`, chevrons `< >`, or apostrophes `'`.
-/// To use it in a `<script>` you can combine it with the safe filter:
-///
-/// ``` html
-/// <script>
-/// var data = {{data|json|safe}};
-/// </script>
-/// ```
-///
-/// To use it in HTML attributes, you can either use it in quotation marks `"{{data|json}}"` as is,
-/// or in apostrophes with the (optional) safe filter `'{{data|json|safe}}'`.
-/// In HTML texts the output of e.g. `<pre>{{data|json|safe}}</pre>` is safe, too.
-pub fn json<S: Serialize>(s: S) -> Result<String> {
- let mut writer = JsonEscapeBuffer::new();
- to_writer_pretty(&mut writer, &s).map_err(Error::from)?;
- Ok(writer.finish())
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_json() {
- assert_eq!(json(true).unwrap(), "true");
- assert_eq!(json("foo").unwrap(), r#""foo""#);
- assert_eq!(json(&true).unwrap(), "true");
- assert_eq!(json(&"foo").unwrap(), r#""foo""#);
- assert_eq!(
- json(&vec!["foo", "bar"]).unwrap(),
- r#"[
- "foo",
- "bar"
-]"#
- );
- }
-}
diff --git a/askama_shared/src/filters/mod.rs b/askama_shared/src/filters/mod.rs
deleted file mode 100644
index 1782602..0000000
--- a/askama_shared/src/filters/mod.rs
+++ /dev/null
@@ -1,636 +0,0 @@
-//! Module for built-in filter functions
-//!
-//! Contains all the built-in filter functions for use in templates.
-//! You can define your own filters, as well.
-//! For more information, read the [book](https://djc.github.io/askama/filters.html).
-#![allow(clippy::trivially_copy_pass_by_ref)]
-
-use std::fmt::{self, Write};
-
-#[cfg(feature = "serde_json")]
-mod json;
-#[cfg(feature = "serde_json")]
-pub use self::json::json;
-
-#[cfg(feature = "serde_yaml")]
-mod yaml;
-#[cfg(feature = "serde_yaml")]
-pub use self::yaml::yaml;
-
-#[allow(unused_imports)]
-use crate::error::Error::Fmt;
-use askama_escape::{Escaper, MarkupDisplay};
-#[cfg(feature = "humansize")]
-use humansize::{file_size_opts, FileSize};
-#[cfg(feature = "num-traits")]
-use num_traits::{cast::NumCast, Signed};
-#[cfg(feature = "percent-encoding")]
-use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC};
-
-use super::Result;
-
-#[cfg(feature = "percent-encoding")]
-// Urlencode char encoding set. Only the characters in the unreserved set don't
-// have any special purpose in any part of a URI and can be safely left
-// unencoded as specified in https://tools.ietf.org/html/rfc3986.html#section-2.3
-const URLENCODE_STRICT_SET: &AsciiSet = &NON_ALPHANUMERIC
- .remove(b'_')
- .remove(b'.')
- .remove(b'-')
- .remove(b'~');
-
-#[cfg(feature = "percent-encoding")]
-// Same as URLENCODE_STRICT_SET, but preserves forward slashes for encoding paths
-const URLENCODE_SET: &AsciiSet = &URLENCODE_STRICT_SET.remove(b'/');
-
-/// Marks a string (or other `Display` type) as safe
-///
-/// Use this is you want to allow markup in an expression, or if you know
-/// that the expression's contents don't need to be escaped.
-///
-/// Askama will automatically insert the first (`Escaper`) argument,
-/// so this filter only takes a single argument of any type that implements
-/// `Display`.
-pub fn safe<E, T>(e: E, v: T) -> Result<MarkupDisplay<E, T>>
-where
- E: Escaper,
- T: fmt::Display,
-{
- Ok(MarkupDisplay::new_safe(v, e))
-}
-
-/// Escapes `&`, `<` and `>` in strings
-///
-/// Askama will automatically insert the first (`Escaper`) argument,
-/// so this filter only takes a single argument of any type that implements
-/// `Display`.
-pub fn escape<E, T>(e: E, v: T) -> Result<MarkupDisplay<E, T>>
-where
- E: Escaper,
- T: fmt::Display,
-{
- Ok(MarkupDisplay::new_unsafe(v, e))
-}
-
-#[cfg(feature = "humansize")]
-/// Returns adequate string representation (in KB, ..) of number of bytes
-pub fn filesizeformat<B: FileSize>(b: &B) -> Result<String> {
- b.file_size(file_size_opts::DECIMAL)
- .map_err(|_| Fmt(fmt::Error))
-}
-
-#[cfg(feature = "percent-encoding")]
-/// Percent-encodes the argument for safe use in URI; does not encode `/`.
-///
-/// This should be safe for all parts of URI (paths segments, query keys, query
-/// values). In the rare case that the server can't deal with forward slashes in
-/// the query string, use [`urlencode_strict`], which encodes them as well.
-///
-/// Encodes all characters except ASCII letters, digits, and `_.-~/`. In other
-/// words, encodes all characters which are not in the unreserved set,
-/// as specified by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.3),
-/// with the exception of `/`.
-///
-/// ```none,ignore
-/// <a href="/metro{{ "/stations/Château d'Eau"|urlencode }}">Station</a>
-/// <a href="/page?text={{ "look, unicode/emojis ✨"|urlencode }}">Page</a>
-/// ```
-///
-/// To encode `/` as well, see [`urlencode_strict`](./fn.urlencode_strict.html).
-///
-/// [`urlencode_strict`]: ./fn.urlencode_strict.html
-pub fn urlencode<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- Ok(utf8_percent_encode(&s, URLENCODE_SET).to_string())
-}
-
-#[cfg(feature = "percent-encoding")]
-/// Percent-encodes the argument for safe use in URI; encodes `/`.
-///
-/// Use this filter for encoding query keys and values in the rare case that
-/// the server can't process them unencoded.
-///
-/// Encodes all characters except ASCII letters, digits, and `_.-~`. In other
-/// words, encodes all characters which are not in the unreserved set,
-/// as specified by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.3).
-///
-/// ```none,ignore
-/// <a href="/page?text={{ "look, unicode/emojis ✨"|urlencode_strict }}">Page</a>
-/// ```
-///
-/// If you want to preserve `/`, see [`urlencode`](./fn.urlencode.html).
-pub fn urlencode_strict<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- Ok(utf8_percent_encode(&s, URLENCODE_STRICT_SET).to_string())
-}
-
-/// Formats arguments according to the specified format
-///
-/// The *second* argument to this filter must be a string literal (as in normal
-/// Rust). The two arguments are passed through to the `format!()`
-/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
-/// the Askama code generator, but the order is swapped to support filter
-/// composition.
-///
-/// ```ignore
-/// {{ value | fmt("{:?}") }}
-/// ```
-///
-/// Compare with [format](./fn.format.html).
-pub fn fmt() {}
-
-/// Formats arguments according to the specified format
-///
-/// The first argument to this filter must be a string literal (as in normal
-/// Rust). All arguments are passed through to the `format!()`
-/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
-/// the Askama code generator.
-///
-/// ```ignore
-/// {{ "{:?}{:?}" | format(value, other_value) }}
-/// ```
-///
-/// Compare with [fmt](./fn.fmt.html).
-pub fn format() {}
-
-/// Replaces line breaks in plain text with appropriate HTML
-///
-/// A single newline becomes an HTML line break `<br>` and a new line
-/// followed by a blank line becomes a paragraph break `<p>`.
-pub fn linebreaks<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- let linebroken = s.replace("\n\n", "</p><p>").replace('\n', "<br/>");
-
- Ok(format!("<p>{}</p>", linebroken))
-}
-
-/// Converts all newlines in a piece of plain text to HTML line breaks
-pub fn linebreaksbr<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- Ok(s.replace('\n', "<br/>"))
-}
-
-/// Replaces only paragraph breaks in plain text with appropriate HTML
-///
-/// A new line followed by a blank line becomes a paragraph break `<p>`.
-/// Paragraph tags only wrap content; empty paragraphs are removed.
-/// No `<br/>` tags are added.
-pub fn paragraphbreaks<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- let linebroken = s.replace("\n\n", "</p><p>").replace("<p></p>", "");
-
- Ok(format!("<p>{}</p>", linebroken))
-}
-
-/// Converts to lowercase
-pub fn lower<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- Ok(s.to_lowercase())
-}
-
-/// Alias for the `lower()` filter
-pub fn lowercase<T: fmt::Display>(s: T) -> Result<String> {
- lower(s)
-}
-
-/// Converts to uppercase
-pub fn upper<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- Ok(s.to_uppercase())
-}
-
-/// Alias for the `upper()` filter
-pub fn uppercase<T: fmt::Display>(s: T) -> Result<String> {
- upper(s)
-}
-
-/// Strip leading and trailing whitespace
-pub fn trim<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- Ok(s.trim().to_owned())
-}
-
-/// Limit string length, appends '...' if truncated
-pub fn truncate<T: fmt::Display>(s: T, len: usize) -> Result<String> {
- let mut s = s.to_string();
- if s.len() > len {
- let mut real_len = len;
- while !s.is_char_boundary(real_len) {
- real_len += 1;
- }
- s.truncate(real_len);
- s.push_str("...");
- }
- Ok(s)
-}
-
-/// Indent lines with `width` spaces
-pub fn indent<T: fmt::Display>(s: T, width: usize) -> Result<String> {
- let s = s.to_string();
-
- let mut indented = String::new();
-
- for (i, c) in s.char_indices() {
- indented.push(c);
-
- if c == '\n' && i < s.len() - 1 {
- for _ in 0..width {
- indented.push(' ');
- }
- }
- }
-
- Ok(indented)
-}
-
-#[cfg(feature = "num-traits")]
-/// Casts number to f64
-pub fn into_f64<T>(number: T) -> Result<f64>
-where
- T: NumCast,
-{
- number.to_f64().ok_or(Fmt(fmt::Error))
-}
-
-#[cfg(feature = "num-traits")]
-/// Casts number to isize
-pub fn into_isize<T>(number: T) -> Result<isize>
-where
- T: NumCast,
-{
- number.to_isize().ok_or(Fmt(fmt::Error))
-}
-
-/// Joins iterable into a string separated by provided argument
-pub fn join<T, I, S>(input: I, separator: S) -> Result<String>
-where
- T: fmt::Display,
- I: Iterator<Item = T>,
- S: AsRef<str>,
-{
- let separator: &str = separator.as_ref();
-
- let mut rv = String::new();
-
- for (num, item) in input.enumerate() {
- if num > 0 {
- rv.push_str(separator);
- }
-
- write!(rv, "{}", item)?;
- }
-
- Ok(rv)
-}
-
-#[cfg(feature = "num-traits")]
-/// Absolute value
-pub fn abs<T>(number: T) -> Result<T>
-where
- T: Signed,
-{
- Ok(number.abs())
-}
-
-/// Capitalize a value. The first character will be uppercase, all others lowercase.
-pub fn capitalize<T: fmt::Display>(s: T) -> Result<String> {
- let s = s.to_string();
- match s.chars().next() {
- Some(c) => {
- let mut replacement: String = c.to_uppercase().collect();
- replacement.push_str(&s[c.len_utf8()..].to_lowercase());
- Ok(replacement)
- }
- _ => Ok(s),
- }
-}
-
-/// Centers the value in a field of a given width
-pub fn center(src: &dyn fmt::Display, dst_len: usize) -> Result<String> {
- let src = src.to_string();
- let len = src.len();
-
- if dst_len <= len {
- Ok(src)
- } else {
- let diff = dst_len - len;
- let mid = diff / 2;
- let r = diff % 2;
- let mut buf = String::with_capacity(dst_len);
-
- for _ in 0..mid {
- buf.push(' ');
- }
-
- buf.push_str(&src);
-
- for _ in 0..mid + r {
- buf.push(' ');
- }
-
- Ok(buf)
- }
-}
-
-/// Count the words in that string
-pub fn wordcount<T: fmt::Display>(s: T) -> Result<usize> {
- let s = s.to_string();
-
- Ok(s.split_whitespace().count())
-}
-
-#[cfg(feature = "markdown")]
-pub fn markdown<E, S>(
- e: E,
- s: S,
- options: Option<&comrak::ComrakOptions>,
-) -> Result<MarkupDisplay<E, String>>
-where
- E: Escaper,
- S: AsRef<str>,
-{
- use comrak::{
- markdown_to_html, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions,
- ComrakRenderOptions,
- };
-
- const DEFAULT_OPTIONS: ComrakOptions = ComrakOptions {
- extension: ComrakExtensionOptions {
- strikethrough: true,
- tagfilter: true,
- table: true,
- autolink: true,
- // default:
- tasklist: false,
- superscript: false,
- header_ids: None,
- footnotes: false,
- description_lists: false,
- front_matter_delimiter: None,
- },
- parse: ComrakParseOptions {
- // default:
- smart: false,
- default_info_string: None,
- },
- render: ComrakRenderOptions {
- unsafe_: false,
- escape: true,
- // default:
- hardbreaks: false,
- github_pre_lang: false,
- width: 0,
- },
- };
-
- let s = markdown_to_html(s.as_ref(), options.unwrap_or(&DEFAULT_OPTIONS));
- Ok(MarkupDisplay::new_safe(s, e))
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- #[cfg(feature = "num-traits")]
- use std::f64::INFINITY;
-
- #[cfg(feature = "humansize")]
- #[test]
- fn test_filesizeformat() {
- assert_eq!(filesizeformat(&0).unwrap(), "0 B");
- assert_eq!(filesizeformat(&999u64).unwrap(), "999 B");
- assert_eq!(filesizeformat(&1000i32).unwrap(), "1 KB");
- assert_eq!(filesizeformat(&1023).unwrap(), "1.02 KB");
- assert_eq!(filesizeformat(&1024usize).unwrap(), "1.02 KB");
- }
-
- #[cfg(feature = "percent-encoding")]
- #[test]
- fn test_urlencoding() {
- // Unreserved (https://tools.ietf.org/html/rfc3986.html#section-2.3)
- // alpha / digit
- assert_eq!(urlencode(&"AZaz09").unwrap(), "AZaz09");
- assert_eq!(urlencode_strict(&"AZaz09").unwrap(), "AZaz09");
- // other
- assert_eq!(urlencode(&"_.-~").unwrap(), "_.-~");
- assert_eq!(urlencode_strict(&"_.-~").unwrap(), "_.-~");
-
- // Reserved (https://tools.ietf.org/html/rfc3986.html#section-2.2)
- // gen-delims
- assert_eq!(urlencode(&":/?#[]@").unwrap(), "%3A/%3F%23%5B%5D%40");
- assert_eq!(
- urlencode_strict(&":/?#[]@").unwrap(),
- "%3A%2F%3F%23%5B%5D%40"
- );
- // sub-delims
- assert_eq!(
- urlencode(&"!$&'()*+,;=").unwrap(),
- "%21%24%26%27%28%29%2A%2B%2C%3B%3D"
- );
- assert_eq!(
- urlencode_strict(&"!$&'()*+,;=").unwrap(),
- "%21%24%26%27%28%29%2A%2B%2C%3B%3D"
- );
-
- // Other
- assert_eq!(
- urlencode(&"žŠďŤňĚáÉóŮ").unwrap(),
- "%C5%BE%C5%A0%C4%8F%C5%A4%C5%88%C4%9A%C3%A1%C3%89%C3%B3%C5%AE"
- );
- assert_eq!(
- urlencode_strict(&"žŠďŤňĚáÉóŮ").unwrap(),
- "%C5%BE%C5%A0%C4%8F%C5%A4%C5%88%C4%9A%C3%A1%C3%89%C3%B3%C5%AE"
- );
-
- // Ferris
- assert_eq!(urlencode(&"🦀").unwrap(), "%F0%9F%A6%80");
- assert_eq!(urlencode_strict(&"🦀").unwrap(), "%F0%9F%A6%80");
- }
-
- #[test]
- fn test_linebreaks() {
- assert_eq!(
- linebreaks(&"Foo\nBar Baz").unwrap(),
- "<p>Foo<br/>Bar Baz</p>"
- );
- assert_eq!(
- linebreaks(&"Foo\nBar\n\nBaz").unwrap(),
- "<p>Foo<br/>Bar</p><p>Baz</p>"
- );
- }
-
- #[test]
- fn test_linebreaksbr() {
- assert_eq!(linebreaksbr(&"Foo\nBar").unwrap(), "Foo<br/>Bar");
- assert_eq!(
- linebreaksbr(&"Foo\nBar\n\nBaz").unwrap(),
- "Foo<br/>Bar<br/><br/>Baz"
- );
- }
-
- #[test]
- fn test_paragraphbreaks() {
- assert_eq!(
- paragraphbreaks(&"Foo\nBar Baz").unwrap(),
- "<p>Foo\nBar Baz</p>"
- );
- assert_eq!(
- paragraphbreaks(&"Foo\nBar\n\nBaz").unwrap(),
- "<p>Foo\nBar</p><p>Baz</p>"
- );
- assert_eq!(
- paragraphbreaks(&"Foo\n\n\n\n\nBar\n\nBaz").unwrap(),
- "<p>Foo</p><p>\nBar</p><p>Baz</p>"
- );
- }
-
- #[test]
- fn test_lower() {
- assert_eq!(lower(&"Foo").unwrap(), "foo");
- assert_eq!(lower(&"FOO").unwrap(), "foo");
- assert_eq!(lower(&"FooBar").unwrap(), "foobar");
- assert_eq!(lower(&"foo").unwrap(), "foo");
- }
-
- #[test]
- fn test_upper() {
- assert_eq!(upper(&"Foo").unwrap(), "FOO");
- assert_eq!(upper(&"FOO").unwrap(), "FOO");
- assert_eq!(upper(&"FooBar").unwrap(), "FOOBAR");
- assert_eq!(upper(&"foo").unwrap(), "FOO");
- }
-
- #[test]
- fn test_trim() {
- assert_eq!(trim(&" Hello\tworld\t").unwrap(), "Hello\tworld");
- }
-
- #[test]
- fn test_truncate() {
- assert_eq!(truncate(&"hello", 2).unwrap(), "he...");
- let a = String::from("您好");
- assert_eq!(a.len(), 6);
- assert_eq!(String::from("您").len(), 3);
- assert_eq!(truncate(&"您好", 1).unwrap(), "您...");
- assert_eq!(truncate(&"您好", 2).unwrap(), "您...");
- assert_eq!(truncate(&"您好", 3).unwrap(), "您...");
- assert_eq!(truncate(&"您好", 4).unwrap(), "您好...");
- assert_eq!(truncate(&"您好", 6).unwrap(), "您好");
- assert_eq!(truncate(&"您好", 7).unwrap(), "您好");
- let s = String::from("🤚a🤚");
- assert_eq!(s.len(), 9);
- assert_eq!(String::from("🤚").len(), 4);
- assert_eq!(truncate(&"🤚a🤚", 1).unwrap(), "🤚...");
- assert_eq!(truncate(&"🤚a🤚", 2).unwrap(), "🤚...");
- assert_eq!(truncate(&"🤚a🤚", 3).unwrap(), "🤚...");
- assert_eq!(truncate(&"🤚a🤚", 4).unwrap(), "🤚...");
- assert_eq!(truncate(&"🤚a🤚", 5).unwrap(), "🤚a...");
- assert_eq!(truncate(&"🤚a🤚", 6).unwrap(), "🤚a🤚...");
- assert_eq!(truncate(&"🤚a🤚", 9).unwrap(), "🤚a🤚");
- assert_eq!(truncate(&"🤚a🤚", 10).unwrap(), "🤚a🤚");
- }
-
- #[test]
- fn test_indent() {
- assert_eq!(indent(&"hello", 2).unwrap(), "hello");
- assert_eq!(indent(&"hello\n", 2).unwrap(), "hello\n");
- assert_eq!(indent(&"hello\nfoo", 2).unwrap(), "hello\n foo");
- assert_eq!(
- indent(&"hello\nfoo\n bar", 4).unwrap(),
- "hello\n foo\n bar"
- );
- }
-
- #[cfg(feature = "num-traits")]
- #[test]
- #[allow(clippy::float_cmp)]
- fn test_into_f64() {
- assert_eq!(into_f64(1).unwrap(), 1.0_f64);
- assert_eq!(into_f64(1.9).unwrap(), 1.9_f64);
- assert_eq!(into_f64(-1.9).unwrap(), -1.9_f64);
- assert_eq!(into_f64(INFINITY as f32).unwrap(), INFINITY);
- assert_eq!(into_f64(-INFINITY as f32).unwrap(), -INFINITY);
- }
-
- #[cfg(feature = "num-traits")]
- #[test]
- fn test_into_isize() {
- assert_eq!(into_isize(1).unwrap(), 1_isize);
- assert_eq!(into_isize(1.9).unwrap(), 1_isize);
- assert_eq!(into_isize(-1.9).unwrap(), -1_isize);
- assert_eq!(into_isize(1.5_f64).unwrap(), 1_isize);
- assert_eq!(into_isize(-1.5_f64).unwrap(), -1_isize);
- match into_isize(INFINITY) {
- Err(Fmt(fmt::Error)) => {}
- _ => panic!("Should return error of type Err(Fmt(fmt::Error))"),
- };
- }
-
- #[allow(clippy::needless_borrow)]
- #[test]
- fn test_join() {
- assert_eq!(
- join((&["hello", "world"]).iter(), ", ").unwrap(),
- "hello, world"
- );
- assert_eq!(join((&["hello"]).iter(), ", ").unwrap(), "hello");
-
- let empty: &[&str] = &[];
- assert_eq!(join(empty.iter(), ", ").unwrap(), "");
-
- let input: Vec<String> = vec!["foo".into(), "bar".into(), "bazz".into()];
- assert_eq!(join(input.iter(), ":").unwrap(), "foo:bar:bazz");
-
- let input: &[String] = &["foo".into(), "bar".into()];
- assert_eq!(join(input.iter(), ":").unwrap(), "foo:bar");
-
- let real: String = "blah".into();
- let input: Vec<&str> = vec![&real];
- assert_eq!(join(input.iter(), ";").unwrap(), "blah");
-
- assert_eq!(
- join((&&&&&["foo", "bar"]).iter(), ", ").unwrap(),
- "foo, bar"
- );
- }
-
- #[cfg(feature = "num-traits")]
- #[test]
- #[allow(clippy::float_cmp)]
- fn test_abs() {
- assert_eq!(abs(1).unwrap(), 1);
- assert_eq!(abs(-1).unwrap(), 1);
- assert_eq!(abs(1.0).unwrap(), 1.0);
- assert_eq!(abs(-1.0).unwrap(), 1.0);
- assert_eq!(abs(1.0_f64).unwrap(), 1.0_f64);
- assert_eq!(abs(-1.0_f64).unwrap(), 1.0_f64);
- }
-
- #[test]
- fn test_capitalize() {
- assert_eq!(capitalize(&"foo").unwrap(), "Foo".to_string());
- assert_eq!(capitalize(&"f").unwrap(), "F".to_string());
- assert_eq!(capitalize(&"fO").unwrap(), "Fo".to_string());
- assert_eq!(capitalize(&"").unwrap(), "".to_string());
- assert_eq!(capitalize(&"FoO").unwrap(), "Foo".to_string());
- assert_eq!(capitalize(&"foO BAR").unwrap(), "Foo bar".to_string());
- assert_eq!(capitalize(&"äØÄÅÖ").unwrap(), "Äøäåö".to_string());
- assert_eq!(capitalize(&"ß").unwrap(), "SS".to_string());
- assert_eq!(capitalize(&"ßß").unwrap(), "SSß".to_string());
- }
-
- #[test]
- fn test_center() {
- assert_eq!(center(&"f", 3).unwrap(), " f ".to_string());
- assert_eq!(center(&"f", 4).unwrap(), " f ".to_string());
- assert_eq!(center(&"foo", 1).unwrap(), "foo".to_string());
- assert_eq!(center(&"foo bar", 8).unwrap(), "foo bar ".to_string());
- }
-
- #[test]
- fn test_wordcount() {
- assert_eq!(wordcount(&"").unwrap(), 0);
- assert_eq!(wordcount(&" \n\t").unwrap(), 0);
- assert_eq!(wordcount(&"foo").unwrap(), 1);
- assert_eq!(wordcount(&"foo bar").unwrap(), 2);
- }
-}
diff --git a/askama_shared/src/filters/yaml.rs b/askama_shared/src/filters/yaml.rs
deleted file mode 100644
index d71e630..0000000
--- a/askama_shared/src/filters/yaml.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use crate::error::{Error, Result};
-use askama_escape::{Escaper, MarkupDisplay};
-use serde::Serialize;
-
-/// Serialize to YAML (requires `serde_yaml` feature)
-///
-/// ## Errors
-///
-/// This will panic if `S`'s implementation of `Serialize` decides to fail,
-/// or if `T` contains a map with non-string keys.
-pub fn yaml<E: Escaper, S: Serialize>(e: E, s: S) -> Result<MarkupDisplay<E, String>> {
- match serde_yaml::to_string(&s) {
- Ok(s) => Ok(MarkupDisplay::new_safe(s, e)),
- Err(e) => Err(Error::from(e)),
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use askama_escape::Html;
-
- #[test]
- fn test_yaml() {
- assert_eq!(yaml(Html, true).unwrap().to_string(), "---\ntrue");
- assert_eq!(yaml(Html, "foo").unwrap().to_string(), "---\nfoo");
- assert_eq!(yaml(Html, &true).unwrap().to_string(), "---\ntrue");
- assert_eq!(yaml(Html, &"foo").unwrap().to_string(), "---\nfoo");
- assert_eq!(
- yaml(Html, &vec!["foo", "bar"]).unwrap().to_string(),
- "---\n- foo\n- bar"
- );
- }
-}
diff --git a/askama_shared/src/helpers/mod.rs b/askama_shared/src/helpers/mod.rs
deleted file mode 100644
index 79a1ada..0000000
--- a/askama_shared/src/helpers/mod.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use std::iter::{Enumerate, Peekable};
-
-pub struct TemplateLoop<I>
-where
- I: Iterator,
-{
- iter: Peekable<Enumerate<I>>,
-}
-
-impl<I> TemplateLoop<I>
-where
- I: Iterator,
-{
- #[inline]
- pub fn new(iter: I) -> Self {
- TemplateLoop {
- iter: iter.enumerate().peekable(),
- }
- }
-}
-
-impl<I> Iterator for TemplateLoop<I>
-where
- I: Iterator,
-{
- type Item = (<I as Iterator>::Item, LoopItem);
-
- #[inline]
- fn next(&mut self) -> Option<(<I as Iterator>::Item, LoopItem)> {
- self.iter.next().map(|(index, item)| {
- (
- item,
- LoopItem {
- index,
- first: index == 0,
- last: self.iter.peek().is_none(),
- },
- )
- })
- }
-}
-
-#[derive(Copy, Clone)]
-pub struct LoopItem {
- pub index: usize,
- pub first: bool,
- pub last: bool,
-}
diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs
deleted file mode 100644
index cb26406..0000000
--- a/askama_shared/src/lib.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-#![cfg_attr(feature = "cargo-clippy", allow(unused_parens))]
-#![forbid(unsafe_code)]
-#![deny(elided_lifetimes_in_paths)]
-#![deny(unreachable_pub)]
-
-use std::fmt;
-
-pub use askama_escape::MarkupDisplay;
-
-mod error;
-pub use crate::error::{Error, Result};
-pub mod filters;
-pub mod helpers;
-
-/// Main `Template` trait; implementations are generally derived
-///
-/// If you need an object-safe template, use [`DynTemplate`].
-pub trait Template: fmt::Display {
- /// Helper method which allocates a new `String` and renders into it
- fn render(&self) -> Result<String> {
- let mut buf = String::with_capacity(Self::SIZE_HINT);
- self.render_into(&mut buf)?;
- Ok(buf)
- }
-
- /// Renders the template to the given `writer` fmt buffer
- fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()>;
-
- /// Renders the template to the given `writer` io buffer
- #[inline]
- fn write_into(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
- writer.write_fmt(format_args!("{}", self))
- }
-
- /// The template's extension, if provided
- const EXTENSION: Option<&'static str>;
-
- /// Provides a conservative estimate of the expanded length of the rendered template
- const SIZE_HINT: usize;
-
- /// The MIME type (Content-Type) of the data that gets rendered by this Template
- const MIME_TYPE: &'static str;
-}
-
-/// Object-safe wrapper trait around [`Template`] implementers
-///
-/// This trades reduced performance (mostly due to writing into `dyn Write`) for object safety.
-pub trait DynTemplate {
- /// Helper method which allocates a new `String` and renders into it
- fn dyn_render(&self) -> Result<String>;
-
- /// Renders the template to the given `writer` fmt buffer
- fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()>;
-
- /// Renders the template to the given `writer` io buffer
- fn dyn_write_into(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()>;
-
- /// Helper function to inspect the template's extension
- fn extension(&self) -> Option<&'static str>;
-
- /// Provides a conservative estimate of the expanded length of the rendered template
- fn size_hint(&self) -> usize;
-
- /// The MIME type (Content-Type) of the data that gets rendered by this Template
- fn mime_type(&self) -> &'static str;
-}
-
-impl<T: Template> DynTemplate for T {
- fn dyn_render(&self) -> Result<String> {
- <Self as Template>::render(self)
- }
-
- fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()> {
- <Self as Template>::render_into(self, writer)
- }
-
- #[inline]
- fn dyn_write_into(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
- writer.write_fmt(format_args!("{}", self))
- }
-
- fn extension(&self) -> Option<&'static str> {
- Self::EXTENSION
- }
-
- fn size_hint(&self) -> usize {
- Self::SIZE_HINT
- }
-
- fn mime_type(&self) -> &'static str {
- Self::MIME_TYPE
- }
-}
-
-impl fmt::Display for dyn DynTemplate {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.dyn_render_into(f).map_err(|_| ::std::fmt::Error {})
- }
-}
-
-#[cfg(test)]
-#[allow(clippy::blacklisted_name)]
-mod tests {
- use std::fmt;
-
- use super::*;
- use crate::{DynTemplate, Template};
-
- #[test]
- fn dyn_template() {
- struct Test;
- impl Template for Test {
- fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()> {
- Ok(writer.write_str("test")?)
- }
-
- const EXTENSION: Option<&'static str> = Some("txt");
-
- const SIZE_HINT: usize = 4;
-
- const MIME_TYPE: &'static str = "text/plain; charset=utf-8";
- }
-
- impl fmt::Display for Test {
- #[inline]
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.render_into(f).map_err(|_| fmt::Error {})
- }
- }
-
- fn render(t: &dyn DynTemplate) -> String {
- t.dyn_render().unwrap()
- }
-
- let test = &Test as &dyn DynTemplate;
-
- assert_eq!(render(test), "test");
-
- assert_eq!(test.to_string(), "test");
-
- assert_eq!(format!("{}", test), "test");
-
- let mut vec = Vec::new();
- test.dyn_write_into(&mut vec).unwrap();
- assert_eq!(vec, vec![b't', b'e', b's', b't']);
- }
-}