pingap_plugin/
lib.rs

1// Copyright 2024-2025 Tree xie.
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 pingap_config::PluginConf;
16use pingap_core::PluginStep;
17use snafu::Snafu;
18use std::str::FromStr;
19
20#[derive(Debug, Snafu)]
21pub enum Error {
22    #[snafu(display("Plugin {category} invalid, message: {message}"))]
23    Invalid { category: String, message: String },
24    #[snafu(display("Plugin {category} not found"))]
25    NotFound { category: String },
26    #[snafu(display("Plugin {category}, base64 decode error {source}"))]
27    Base64Decode {
28        category: String,
29        source: base64::DecodeError,
30    },
31    #[snafu(display("Plugin {category}, exceed limit {value}/{max}"))]
32    Exceed {
33        category: String,
34        max: isize,
35        value: isize,
36    },
37    #[snafu(display("Plugin {category}, regex error {source}"))]
38    Regex {
39        category: String,
40        source: Box<fancy_regex::Error>,
41    },
42    #[snafu(display("Plugin {category}, base64 decode error {source}"))]
43    ParseDuration {
44        category: String,
45        source: humantime::DurationError,
46    },
47}
48
49/// Helper functions for accessing plugin configuration values
50pub fn get_str_conf(value: &PluginConf, key: &str) -> String {
51    if let Some(value) = value.get(key) {
52        value.as_str().unwrap_or_default().to_string()
53    } else {
54        "".to_string()
55    }
56}
57
58pub(crate) fn get_str_slice_conf(value: &PluginConf, key: &str) -> Vec<String> {
59    if let Some(value) = value.get(key) {
60        if let Some(values) = value.as_array() {
61            return values
62                .iter()
63                .map(|item| item.as_str().unwrap_or_default().to_string())
64                .collect();
65        }
66    }
67    vec![]
68}
69
70pub(crate) fn get_bool_conf(value: &PluginConf, key: &str) -> bool {
71    if let Some(value) = value.get(key) {
72        value.as_bool().unwrap_or_default()
73    } else {
74        false
75    }
76}
77
78pub fn get_int_conf(value: &PluginConf, key: &str) -> i64 {
79    if let Some(value) = value.get(key) {
80        value.as_integer().unwrap_or_default()
81    } else {
82        0
83    }
84}
85
86pub(crate) fn get_step_conf(
87    value: &PluginConf,
88    default_value: PluginStep,
89) -> PluginStep {
90    let step = get_str_conf(value, "step");
91    if step.is_empty() {
92        return default_value;
93    }
94
95    PluginStep::from_str(step.as_str()).unwrap_or(default_value)
96}
97
98/// Generates a unique hash key for a plugin configuration to detect changes.
99///
100/// # Arguments
101/// * `conf` - The plugin configuration to hash
102///
103/// # Returns
104/// A string containing the CRC32 hash of the sorted configuration key-value pairs
105pub fn get_hash_key(conf: &PluginConf) -> String {
106    let mut keys: Vec<String> =
107        conf.keys().map(|item| item.to_string()).collect();
108    keys.sort();
109    let mut lines = vec![];
110    for key in keys {
111        let value = if let Some(value) = conf.get(&key) {
112            value.to_string()
113        } else {
114            "".to_string()
115        };
116        lines.push(format!("{key}:{value}"));
117    }
118    let hash = crc32fast::hash(lines.join("\n").as_bytes());
119    format!("{hash:X}")
120}
121
122mod accept_encoding;
123mod basic_auth;
124mod cache;
125mod combined_auth;
126mod compression;
127mod cors;
128mod csrf;
129mod directory;
130mod ip_restriction;
131mod jwt;
132mod key_auth;
133mod limit;
134mod mock;
135mod ping;
136mod redirect;
137mod referer_restriction;
138mod request_id;
139mod response_headers;
140mod sub_filter;
141mod ua_restriction;
142
143mod plugin;
144
145pub use plugin::get_plugin_factory;