1use std::fmt;
9
10use serde::{Deserialize, Serialize};
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
19pub enum ServerError {
20 App(String),
22 Unauthorized(String),
24 Forbidden(String),
26 BadRequest(String),
28 Network(String),
30}
31
32impl fmt::Display for ServerError {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 match self {
35 ServerError::App(msg) => write!(f, "server error: {msg}"),
36 ServerError::Unauthorized(msg) => write!(f, "unauthorized: {msg}"),
37 ServerError::Forbidden(msg) => write!(f, "forbidden: {msg}"),
38 ServerError::BadRequest(msg) => write!(f, "bad request: {msg}"),
39 ServerError::Network(msg) => write!(f, "network error: {msg}"),
40 }
41 }
42}
43
44impl std::error::Error for ServerError {}
45
46impl ServerError {
47 pub fn unauthorized(msg: impl Into<String>) -> Self {
49 ServerError::Unauthorized(msg.into())
50 }
51
52 pub fn forbidden(msg: impl Into<String>) -> Self {
54 ServerError::Forbidden(msg.into())
55 }
56
57 pub fn bad_request(msg: impl Into<String>) -> Self {
59 ServerError::BadRequest(msg.into())
60 }
61}
62
63impl From<String> for ServerError {
64 fn from(s: String) -> Self {
65 ServerError::App(s)
66 }
67}
68
69impl From<&str> for ServerError {
70 fn from(s: &str) -> Self {
71 ServerError::App(s.to_owned())
72 }
73}
74
75pub type Result<T> = core::result::Result<T, ServerError>;
77
78const SERVER_FUNCTION_HASH_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
79const SERVER_FUNCTION_HASH_PRIME: u64 = 0x0000_0100_0000_01b3;
80
81fn fnv1a64(bytes: &[u8]) -> u64 {
82 let mut hash = SERVER_FUNCTION_HASH_OFFSET;
83 for byte in bytes {
84 hash ^= u64::from(*byte);
85 hash = hash.wrapping_mul(SERVER_FUNCTION_HASH_PRIME);
86 }
87 hash
88}
89
90#[doc(hidden)]
95pub fn server_function_default_path(module_path: &str, function: &str) -> String {
96 let mut key = String::with_capacity(module_path.len() + function.len() + 2);
97 key.push_str(module_path);
98 key.push_str("::");
99 key.push_str(function);
100 let hash = fnv1a64(key.as_bytes());
101
102 format!("/_pocopine/{function}_{hash:016x}")
103}
104
105#[cfg(test)]
106mod tests {
107 use super::server_function_default_path;
108
109 #[test]
110 fn default_paths_are_scoped_by_module_path() {
111 let first = server_function_default_path("app::filters", "get_filter");
112 let second = server_function_default_path("app::admin", "get_filter");
113
114 assert_ne!(first, second);
115 assert!(first.starts_with("/_pocopine/get_filter_"));
116 assert!(second.starts_with("/_pocopine/get_filter_"));
117 }
118
119 #[test]
120 fn default_paths_are_stable() {
121 assert_eq!(
122 server_function_default_path("app::filters", "get_filter"),
123 "/_pocopine/get_filter_b0e2ae5de9927498"
124 );
125 }
126}