Skip to main content

http_handle/
optimized.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (c) 2026 Sebastien Rousseau
3
4//! Zero-allocation lookup helpers for hot-path operations.
5
6use crate::language::Language;
7
8/// Small stack-backed language set.
9///
10/// # Examples
11///
12/// ```rust
13/// use http_handle::optimized::LanguageSet;
14/// let set = LanguageSet::new();
15/// assert!(set.as_slice().is_empty());
16/// ```
17///
18/// # Panics
19///
20/// This type does not panic.
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub struct LanguageSet {
23    data: [Language; 8],
24    len: usize,
25}
26
27impl LanguageSet {
28    /// Creates an empty set.
29    ///
30    /// # Examples
31    ///
32    /// ```rust
33    /// use http_handle::optimized::LanguageSet;
34    /// let set = LanguageSet::new();
35    /// assert_eq!(set.as_slice().len(), 0);
36    /// ```
37    ///
38    /// # Panics
39    ///
40    /// This function does not panic.
41    pub const fn new() -> Self {
42        Self {
43            data: [Language::Unknown; 8],
44            len: 0,
45        }
46    }
47
48    /// Inserts a language if absent and capacity allows.
49    ///
50    /// # Examples
51    ///
52    /// ```rust
53    /// use http_handle::language::Language;
54    /// use http_handle::optimized::LanguageSet;
55    /// let mut set = LanguageSet::new();
56    /// set.insert(Language::Rust);
57    /// assert!(set.contains(Language::Rust));
58    /// ```
59    ///
60    /// # Panics
61    ///
62    /// This function does not panic.
63    pub fn insert(&mut self, language: Language) {
64        if self.contains(language) || self.len >= self.data.len() {
65            return;
66        }
67        self.data[self.len] = language;
68        self.len += 1;
69    }
70
71    /// Returns true if language is present.
72    ///
73    /// # Examples
74    ///
75    /// ```rust
76    /// use http_handle::language::Language;
77    /// use http_handle::optimized::LanguageSet;
78    /// let mut set = LanguageSet::new();
79    /// set.insert(Language::Go);
80    /// assert!(set.contains(Language::Go));
81    /// ```
82    ///
83    /// # Panics
84    ///
85    /// This function does not panic.
86    pub fn contains(&self, language: Language) -> bool {
87        self.data[..self.len].contains(&language)
88    }
89
90    /// Returns slice view over inserted languages.
91    ///
92    /// # Examples
93    ///
94    /// ```rust
95    /// use http_handle::language::Language;
96    /// use http_handle::optimized::LanguageSet;
97    /// let mut set = LanguageSet::new();
98    /// set.insert(Language::Python);
99    /// assert_eq!(set.as_slice(), &[Language::Python]);
100    /// ```
101    ///
102    /// # Panics
103    ///
104    /// This function does not panic.
105    pub fn as_slice(&self) -> &[Language] {
106        &self.data[..self.len]
107    }
108}
109
110impl Default for LanguageSet {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116/// Branch-optimized extension to content type lookup for hot paths.
117///
118/// # Examples
119///
120/// ```rust
121/// use http_handle::optimized::const_content_type_from_ext;
122/// assert_eq!(const_content_type_from_ext("wasm"), "application/wasm");
123/// ```
124///
125/// # Panics
126///
127/// This function does not panic.
128pub fn const_content_type_from_ext(ext: &str) -> &'static str {
129    match ext {
130        "html" | "htm" => "text/html",
131        "css" => "text/css",
132        "js" | "mjs" => "application/javascript",
133        "json" => "application/json",
134        "wasm" => "application/wasm",
135        "webp" => "image/webp",
136        "avif" => "image/avif",
137        _ => "application/octet-stream",
138    }
139}
140
141/// Fast no-allocation language hinting using substring checks only.
142///
143/// # Examples
144///
145/// ```rust
146/// use http_handle::language::Language;
147/// use http_handle::optimized::detect_language_fast;
148/// assert_eq!(detect_language_fast("fn main() {}"), Language::Rust);
149/// ```
150///
151/// # Panics
152///
153/// This function does not panic.
154pub fn detect_language_fast(input: &str) -> Language {
155    if input.contains("fn ")
156        || input.contains("impl ")
157        || input.contains("let ")
158    {
159        return Language::Rust;
160    }
161    if input.contains("def ") || input.contains("import ") {
162        return Language::Python;
163    }
164    if input.contains("function ")
165        || input.contains("const ")
166        || input.contains("=>")
167    {
168        return Language::JavaScript;
169    }
170    if input.contains("package ") || input.contains("func ") {
171        return Language::Go;
172    }
173    Language::Unknown
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn set_is_stack_based_and_deduplicated() {
182        let mut set = LanguageSet::new();
183        set.insert(Language::Rust);
184        set.insert(Language::Rust);
185        set.insert(Language::Python);
186        assert_eq!(set.as_slice(), &[Language::Rust, Language::Python]);
187    }
188
189    #[test]
190    fn detects_const_content_types() {
191        assert_eq!(
192            const_content_type_from_ext("wasm"),
193            "application/wasm"
194        );
195        assert_eq!(
196            const_content_type_from_ext("unknown"),
197            "application/octet-stream"
198        );
199    }
200
201    #[test]
202    fn detects_fast_language_paths() {
203        assert_eq!(
204            detect_language_fast("fn main() {}"),
205            Language::Rust
206        );
207        assert_eq!(
208            detect_language_fast("def main(): pass"),
209            Language::Python
210        );
211        assert_eq!(
212            detect_language_fast("const x = () => 1;"),
213            Language::JavaScript
214        );
215        assert_eq!(
216            detect_language_fast("package main\nfunc main() {}"),
217            Language::Go
218        );
219        assert_eq!(
220            detect_language_fast("plain text"),
221            Language::Unknown
222        );
223    }
224
225    #[test]
226    fn set_capacity_is_bounded() {
227        let mut set = LanguageSet::new();
228        for _ in 0..16 {
229            set.insert(Language::Rust);
230            set.insert(Language::Python);
231            set.insert(Language::JavaScript);
232            set.insert(Language::Go);
233            set.insert(Language::Unknown);
234        }
235        assert!(set.as_slice().len() <= 8);
236    }
237
238    #[test]
239    fn default_matches_new_set() {
240        let via_default = LanguageSet::default();
241        let via_new = LanguageSet::new();
242        assert_eq!(via_default.as_slice(), via_new.as_slice());
243    }
244}