liquid_lib/stdlib/filters/
url.rs1use liquid_core::Result;
2use liquid_core::Runtime;
3use liquid_core::{Display_filter, Filter, FilterReflection, ParseFilter};
4use liquid_core::{Value, ValueView};
5
6use crate::invalid_input;
7
8const FRAGMENT: &percent_encoding::AsciiSet = &percent_encoding::NON_ALPHANUMERIC
9 .remove(b'-')
10 .remove(b'.')
11 .remove(b'_');
12
13#[derive(Clone, ParseFilter, FilterReflection)]
14#[filter(
15 name = "url_encode",
16 description = "Converts any URL-unsafe characters in a string into percent-encoded characters.",
17 parsed(UrlEncodeFilter)
18)]
19pub struct UrlEncode;
20
21#[derive(Debug, Default, Display_filter)]
22#[name = "url_encode"]
23struct UrlEncodeFilter;
24
25impl Filter for UrlEncodeFilter {
26 fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result<Value> {
27 if input.is_nil() {
28 return Ok(Value::Nil);
29 }
30
31 let s = input.to_kstr();
32
33 let result: String = percent_encoding::utf8_percent_encode(s.as_str(), FRAGMENT).collect();
34 Ok(Value::scalar(result))
35 }
36}
37
38#[derive(Clone, ParseFilter, FilterReflection)]
39#[filter(
40 name = "url_decode",
41 description = "Decodes a string that has been encoded as a URL or by url_encode.",
42 parsed(UrlDecodeFilter)
43)]
44pub struct UrlDecode;
45
46#[derive(Debug, Default, Display_filter)]
47#[name = "url_decode"]
48struct UrlDecodeFilter;
49
50impl Filter for UrlDecodeFilter {
51 fn evaluate(&self, input: &dyn ValueView, _runtime: &dyn Runtime) -> Result<Value> {
52 if input.is_nil() {
53 return Ok(Value::Nil);
54 }
55
56 let s = input.to_kstr().replace('+', " ");
57
58 let result = percent_encoding::percent_decode(s.as_bytes())
59 .decode_utf8()
60 .map_err(|_| invalid_input("Malformed UTF-8"))?
61 .into_owned();
62 Ok(Value::scalar(result))
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn unit_url_encode() {
72 assert_eq!(
73 liquid_core::call_filter!(UrlEncode, "foo bar").unwrap(),
74 liquid_core::value!("foo%20bar")
75 );
76 assert_eq!(
77 liquid_core::call_filter!(UrlEncode, "foo+1@example.com").unwrap(),
78 liquid_core::value!("foo%2B1%40example.com")
79 );
80 }
81
82 #[test]
83 fn unit_url_decode() {
84 assert_eq!(
90 liquid_core::call_filter!(UrlDecode, "foo%20bar").unwrap(),
91 liquid_core::value!("foo bar")
92 );
93 assert_eq!(
94 liquid_core::call_filter!(UrlDecode, "foo%2B1%40example.com").unwrap(),
95 liquid_core::value!("foo+1@example.com")
96 );
97 }
98}