common_function/
utils.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::hash::BuildHasher;
16
17use ahash::RandomState;
18use serde::{Deserialize, Serialize};
19
20/// Escapes special characters in the provided pattern string for `LIKE`.
21///
22/// Specifically, it prefixes the backslash (`\`), percent (`%`), and underscore (`_`)
23/// characters with an additional backslash to ensure they are treated literally.
24///
25/// # Examples
26///
27/// ```rust
28/// let escaped = escape_pattern("100%_some\\path");
29/// assert_eq!(escaped, "100\\%\\_some\\\\path");
30/// ```
31pub fn escape_like_pattern(pattern: &str) -> String {
32    pattern
33        .chars()
34        .flat_map(|c| match c {
35            '\\' | '%' | '_' => vec!['\\', c],
36            _ => vec![c],
37        })
38        .collect::<String>()
39}
40
41/// A random state with fixed seeds.
42///
43/// This is used to ensure that the hash values are consistent across
44/// different processes, and easy to serialize and deserialize.
45#[derive(Debug)]
46pub struct FixedRandomState {
47    state: RandomState,
48}
49
50impl FixedRandomState {
51    // some random seeds
52    const RANDOM_SEED_0: u64 = 0x517cc1b727220a95;
53    const RANDOM_SEED_1: u64 = 0x428a2f98d728ae22;
54    const RANDOM_SEED_2: u64 = 0x7137449123ef65cd;
55    const RANDOM_SEED_3: u64 = 0xb5c0fbcfec4d3b2f;
56
57    pub fn new() -> Self {
58        Self {
59            state: ahash::RandomState::with_seeds(
60                Self::RANDOM_SEED_0,
61                Self::RANDOM_SEED_1,
62                Self::RANDOM_SEED_2,
63                Self::RANDOM_SEED_3,
64            ),
65        }
66    }
67}
68
69impl Default for FixedRandomState {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl BuildHasher for FixedRandomState {
76    type Hasher = ahash::AHasher;
77
78    fn build_hasher(&self) -> Self::Hasher {
79        self.state.build_hasher()
80    }
81
82    fn hash_one<T: std::hash::Hash>(&self, x: T) -> u64 {
83        self.state.hash_one(x)
84    }
85}
86
87impl Serialize for FixedRandomState {
88    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
89    where
90        S: serde::Serializer,
91    {
92        serializer.serialize_unit()
93    }
94}
95
96impl<'de> Deserialize<'de> for FixedRandomState {
97    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
98    where
99        D: serde::Deserializer<'de>,
100    {
101        Ok(Self::new())
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_escape_like_pattern() {
111        assert_eq!(
112            escape_like_pattern("100%_some\\path"),
113            "100\\%\\_some\\\\path"
114        );
115        assert_eq!(escape_like_pattern(""), "");
116        assert_eq!(escape_like_pattern("hello"), "hello");
117        assert_eq!(escape_like_pattern("\\%_"), "\\\\\\%\\_");
118        assert_eq!(escape_like_pattern("%%__\\\\"), "\\%\\%\\_\\_\\\\\\\\");
119        assert_eq!(escape_like_pattern("abc123"), "abc123");
120        assert_eq!(escape_like_pattern("%_\\"), "\\%\\_\\\\");
121        assert_eq!(
122            escape_like_pattern("%%__\\\\another%string"),
123            "\\%\\%\\_\\_\\\\\\\\another\\%string"
124        );
125        assert_eq!(escape_like_pattern("foo%bar_"), "foo\\%bar\\_");
126        assert_eq!(escape_like_pattern("\\_\\%"), "\\\\\\_\\\\\\%");
127    }
128}