use super::FilterFunction;
use crate::functions::metadata::{ArgumentMetadata, FunctionMetadata, SyntaxVariants};
use minijinja::value::Kwargs;
use minijinja::{Error, ErrorKind, Value};
const STRING_ARG: ArgumentMetadata = ArgumentMetadata {
name: "string",
arg_type: "string",
required: true,
default: None,
description: "The string to process",
};
pub struct Base64Encode;
impl Base64Encode {
fn encode(input: &str) -> String {
base64::Engine::encode(&base64::engine::general_purpose::STANDARD, input.as_bytes())
}
}
impl FilterFunction for Base64Encode {
const NAME: &'static str = "base64_encode";
const METADATA: FunctionMetadata = FunctionMetadata {
name: "base64_encode",
category: "encoding",
description: "Encode a string to Base64",
arguments: &[STRING_ARG],
return_type: "string",
examples: &[
"{{ base64_encode(string=\"hello\") }}",
"{{ \"hello\" | base64_encode }}",
],
syntax: SyntaxVariants::FUNCTION_AND_FILTER,
};
fn call_as_function(kwargs: Kwargs) -> Result<Value, Error> {
let input: String = kwargs.get("string")?;
Ok(Value::from(Self::encode(&input)))
}
fn call_as_filter(value: &Value, _kwargs: Kwargs) -> Result<Value, Error> {
let input = value.as_str().ok_or_else(|| {
Error::new(
ErrorKind::InvalidOperation,
"base64_encode requires a string",
)
})?;
Ok(Value::from(Self::encode(input)))
}
}
pub struct Base64Decode;
impl Base64Decode {
fn decode(input: &str) -> Result<String, Error> {
let decoded_bytes =
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, input.as_bytes())
.map_err(|e| {
Error::new(
ErrorKind::InvalidOperation,
format!("Failed to decode base64: {}", e),
)
})?;
String::from_utf8(decoded_bytes).map_err(|e| {
Error::new(
ErrorKind::InvalidOperation,
format!("Decoded base64 is not valid UTF-8: {}", e),
)
})
}
}
impl FilterFunction for Base64Decode {
const NAME: &'static str = "base64_decode";
const METADATA: FunctionMetadata = FunctionMetadata {
name: "base64_decode",
category: "encoding",
description: "Decode a Base64 string",
arguments: &[STRING_ARG],
return_type: "string",
examples: &[
"{{ base64_decode(string=\"aGVsbG8=\") }}",
"{{ \"aGVsbG8=\" | base64_decode }}",
],
syntax: SyntaxVariants::FUNCTION_AND_FILTER,
};
fn call_as_function(kwargs: Kwargs) -> Result<Value, Error> {
let input: String = kwargs.get("string")?;
Ok(Value::from(Self::decode(&input)?))
}
fn call_as_filter(value: &Value, _kwargs: Kwargs) -> Result<Value, Error> {
let input = value.as_str().ok_or_else(|| {
Error::new(
ErrorKind::InvalidOperation,
"base64_decode requires a string",
)
})?;
Ok(Value::from(Self::decode(input)?))
}
}
pub struct HexEncode;
impl HexEncode {
fn encode(input: &str) -> String {
hex::encode(input.as_bytes())
}
}
impl FilterFunction for HexEncode {
const NAME: &'static str = "hex_encode";
const METADATA: FunctionMetadata = FunctionMetadata {
name: "hex_encode",
category: "encoding",
description: "Encode a string to hexadecimal",
arguments: &[STRING_ARG],
return_type: "string",
examples: &[
"{{ hex_encode(string=\"hello\") }}",
"{{ \"hello\" | hex_encode }}",
],
syntax: SyntaxVariants::FUNCTION_AND_FILTER,
};
fn call_as_function(kwargs: Kwargs) -> Result<Value, Error> {
let input: String = kwargs.get("string")?;
Ok(Value::from(Self::encode(&input)))
}
fn call_as_filter(value: &Value, _kwargs: Kwargs) -> Result<Value, Error> {
let input = value.as_str().ok_or_else(|| {
Error::new(ErrorKind::InvalidOperation, "hex_encode requires a string")
})?;
Ok(Value::from(Self::encode(input)))
}
}
pub struct HexDecode;
impl HexDecode {
fn decode(input: &str) -> Result<String, Error> {
let decoded_bytes = hex::decode(input).map_err(|e| {
Error::new(
ErrorKind::InvalidOperation,
format!("Failed to decode hex: {}", e),
)
})?;
String::from_utf8(decoded_bytes).map_err(|e| {
Error::new(
ErrorKind::InvalidOperation,
format!("Decoded hex is not valid UTF-8: {}", e),
)
})
}
}
impl FilterFunction for HexDecode {
const NAME: &'static str = "hex_decode";
const METADATA: FunctionMetadata = FunctionMetadata {
name: "hex_decode",
category: "encoding",
description: "Decode a hexadecimal string",
arguments: &[STRING_ARG],
return_type: "string",
examples: &[
"{{ hex_decode(string=\"68656c6c6f\") }}",
"{{ \"68656c6c6f\" | hex_decode }}",
],
syntax: SyntaxVariants::FUNCTION_AND_FILTER,
};
fn call_as_function(kwargs: Kwargs) -> Result<Value, Error> {
let input: String = kwargs.get("string")?;
Ok(Value::from(Self::decode(&input)?))
}
fn call_as_filter(value: &Value, _kwargs: Kwargs) -> Result<Value, Error> {
let input = value.as_str().ok_or_else(|| {
Error::new(ErrorKind::InvalidOperation, "hex_decode requires a string")
})?;
Ok(Value::from(Self::decode(input)?))
}
}
pub struct EscapeHtml;
impl EscapeHtml {
fn escape(input: &str) -> String {
input
.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
}
impl FilterFunction for EscapeHtml {
const NAME: &'static str = "escape_html";
const METADATA: FunctionMetadata = FunctionMetadata {
name: "escape_html",
category: "encoding",
description: "Escape HTML special characters",
arguments: &[STRING_ARG],
return_type: "string",
examples: &[
"{{ escape_html(string=\"<script>alert('xss')</script>\") }}",
"{{ \"<div>\" | escape_html }}",
],
syntax: SyntaxVariants::FUNCTION_AND_FILTER,
};
fn call_as_function(kwargs: Kwargs) -> Result<Value, Error> {
let input: String = kwargs.get("string")?;
Ok(Value::from(Self::escape(&input)))
}
fn call_as_filter(value: &Value, _kwargs: Kwargs) -> Result<Value, Error> {
let input = value.as_str().ok_or_else(|| {
Error::new(ErrorKind::InvalidOperation, "escape_html requires a string")
})?;
Ok(Value::from(Self::escape(input)))
}
}
pub struct EscapeXml;
impl EscapeXml {
fn escape(input: &str) -> String {
input
.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
}
impl FilterFunction for EscapeXml {
const NAME: &'static str = "escape_xml";
const METADATA: FunctionMetadata = FunctionMetadata {
name: "escape_xml",
category: "encoding",
description: "Escape XML special characters",
arguments: &[STRING_ARG],
return_type: "string",
examples: &[
"{{ escape_xml(string=\"<tag attr='value'>\") }}",
"{{ \"<xml>\" | escape_xml }}",
],
syntax: SyntaxVariants::FUNCTION_AND_FILTER,
};
fn call_as_function(kwargs: Kwargs) -> Result<Value, Error> {
let input: String = kwargs.get("string")?;
Ok(Value::from(Self::escape(&input)))
}
fn call_as_filter(value: &Value, _kwargs: Kwargs) -> Result<Value, Error> {
let input = value.as_str().ok_or_else(|| {
Error::new(ErrorKind::InvalidOperation, "escape_xml requires a string")
})?;
Ok(Value::from(Self::escape(input)))
}
}
pub struct EscapeShell;
impl EscapeShell {
fn escape(input: &str) -> String {
let escaped = input.replace('\'', "'\\''");
format!("'{}'", escaped)
}
}
impl FilterFunction for EscapeShell {
const NAME: &'static str = "escape_shell";
const METADATA: FunctionMetadata = FunctionMetadata {
name: "escape_shell",
category: "encoding",
description: "Escape shell special characters for safe command execution",
arguments: &[STRING_ARG],
return_type: "string",
examples: &[
"{{ escape_shell(string=\"file name.txt\") }}",
"{{ user_input | escape_shell }}",
],
syntax: SyntaxVariants::FUNCTION_AND_FILTER,
};
fn call_as_function(kwargs: Kwargs) -> Result<Value, Error> {
let input: String = kwargs.get("string")?;
Ok(Value::from(Self::escape(&input)))
}
fn call_as_filter(value: &Value, _kwargs: Kwargs) -> Result<Value, Error> {
let input = value.as_str().ok_or_else(|| {
Error::new(
ErrorKind::InvalidOperation,
"escape_shell requires a string",
)
})?;
Ok(Value::from(Self::escape(input)))
}
}