1use crate::compiler::prelude::*;
2
3#[allow(clippy::cast_possible_wrap)]
4fn seahash(value: Value) -> Resolved {
5 let value = value.try_bytes()?;
6 Ok(Value::Integer(seahash::hash(&value) as i64))
7}
8
9#[derive(Clone, Copy, Debug)]
10pub struct Seahash;
11
12impl Function for Seahash {
13 fn identifier(&self) -> &'static str {
14 "seahash"
15 }
16
17 fn usage(&self) -> &'static str {
18 indoc! {"
19 Calculates a [Seahash](https://docs.rs/seahash/latest/seahash/) hash of the `value`.
20 **Note**: Due to limitations in the underlying VRL data types, this function converts the unsigned 64-bit integer SeaHash result to a signed 64-bit integer. Results higher than the signed 64-bit integer maximum value wrap around to negative values.
21 "}
22 }
23
24 fn category(&self) -> &'static str {
25 Category::Cryptography.as_ref()
26 }
27
28 fn return_kind(&self) -> u16 {
29 kind::INTEGER
30 }
31
32 fn examples(&self) -> &'static [Example] {
33 &[
34 example! {
35 title: "Calculate seahash",
36 source: r#"seahash("foobar")"#,
37 result: Ok("5348458858952426560"),
38 },
39 example! {
40 title: "Calculate negative seahash",
41 source: r#"seahash("bar")"#,
42 result: Ok("-2796170501982571315"),
43 },
44 ]
45 }
46
47 fn compile(
48 &self,
49 _state: &state::TypeState,
50 _ctx: &mut FunctionCompileContext,
51 arguments: ArgumentList,
52 ) -> Compiled {
53 let value = arguments.required("value");
54
55 Ok(SeahashFn { value }.as_expr())
56 }
57
58 fn parameters(&self) -> &'static [Parameter] {
59 const PARAMETERS: &[Parameter] = &[Parameter::required(
60 "value",
61 kind::BYTES,
62 "The string to calculate the hash for.",
63 )];
64 PARAMETERS
65 }
66}
67
68#[derive(Debug, Clone)]
69struct SeahashFn {
70 value: Box<dyn Expression>,
71}
72
73impl FunctionExpression for SeahashFn {
74 fn resolve(&self, ctx: &mut Context) -> Resolved {
75 let value = self.value.resolve(ctx)?;
76 seahash(value)
77 }
78
79 fn type_def(&self, _: &state::TypeState) -> TypeDef {
80 TypeDef::integer().infallible()
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 test_function![
89 seahash => Seahash;
90
91 seahash {
92 args: func_args![value: "foo"],
93 want: Ok(4_413_582_353_838_009_230_i64),
94 tdef: TypeDef::integer().infallible(),
95 }
96
97 seahash_buffer_overflow {
98 args: func_args![value: "bar"],
99 want: Ok(-2_796_170_501_982_571_315_i64),
100 tdef: TypeDef::integer().infallible(),
101 }
102 ];
103}