Repository: jaytaph/gosub-browser
File: crates/gosub_net/src/http/headers.rs (Rust)

Code before:
use std::collections::HashMap;

#[derive(Default, Debug, Clone)]
pub struct Headers {
    headers: HashMap<String, String>,
}

impl Headers {
    pub fn new() -> Headers {
        Headers {
            headers: HashMap::new(),
        }
    }

    pub fn set(&mut self, key: &str, value: &str) {
        self.headers.insert(key.to_string(), value.to_string());
    }

    pub fn get(&self, key: &str) -> Option<&String> {
        self.headers.get(key)
    }

    /// Returns all the header entries. Note that there is no ordering in here!
    pub fn all(&self) -> &HashMap<String, String> {
        &self.headers
    }

    pub fn sorted(&self) -> Vec<(&String, &String)> {
        let mut sorted = self.headers.iter().collect::<Vec<_>>();
        sorted.sort_by(|a, b| a.0.cmp(b.0));
        sorted
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_headers() {
        let mut headers = Headers::new();

        headers.set("Content-Type", "application/json");
        assert_eq!(headers.get("Content-Type").unwrap(), "application/json");

        headers.set("Content-Type", "text/html");

Review comment (performance):
Hello hello, once again a random comment after long silence 😉 I am actually very actively following the activity here on GitHub, but don't have a lot of time for communication. This has already been merged, but a little note for the future: Rust allows you to use `impl Trait` types as parameters, so you don't need separate methods `set` and `set_str`. That is widely used in the standard library too. The method would look like this:
```rs
pub fn set(&mut self, key: impl AsRef<str>, value: impl AsRef<str>) {
    self.headers.insert(key.as_ref().to_string(), value.as_ref().to_string());
}
```
When calling it with owned strings, the compiler will completely optimize this away into just `self.headers.insert(key, value);`. Because `impl` is just syntax sugar for bounded generics, the method actually gets monomorphized, resulting in the same 2 methods, but you are able to "generate" more by calling it with `&str, String` for example.
```rs
req.header.set("Content-Type", format!("text/{extension}")); // bad, don't do this, just an example
```

Code after:
use std::collections::HashMap;

#[derive(Default, Debug, Clone)]
pub struct Headers {
    headers: HashMap<String, String>,
}

impl Headers {
    pub fn new() -> Headers {
        Headers {
            headers: HashMap::new(),
        }
    }

    pub fn with_capacity(capacity: usize) -> Headers {
        Headers {
            headers: HashMap::with_capacity(capacity),
        }
    }

    pub fn set_str(&mut self, key: &str, value: &str) {
        self.headers.insert(key.to_string(), value.to_string());
    }

    pub fn set(&mut self, key: String, value: String) {
        self.headers.insert(key, value);
    }

    pub fn get(&self, key: &str) -> Option<&String> {
        self.headers.get(key)
    }

    /// Returns all the header entries. Note that there is no ordering in here!
    pub fn all(&self) -> &HashMap<String, String> {
        &self.headers
    }

    pub fn sorted(&self) -> Vec<(&String, &String)> {
        let mut sorted = self.headers.iter().collect::<Vec<_>>();
        sorted.sort_by(|a, b| a.0.cmp(b.0));
        sorted
    }
}

#[cfg(test)]
mod tests {