Struct to Hashmap or Vector of tuples

Hello.

I am looking at using a Rust library in another language using existing libraries to convert between the data types. For example Rust HashMap to another language Hash, but this is already something well defined and not something I need to look into.

I have been looking at cooke parsing which takes an input of a string and returns a cookie struct. Several libraries seem to do this. I was wondering if I could convert this to a simpler structure, or ignore the struct. I see it as either a HashMap of key, value strings, or could be a vector of tuples. This would convert well with the libraries for other languages. If it was being used in Rust I could expect a cookie struct to be returned but not familiar with how I can return either something simpler or ignore this in the return type.

use basic_cookies::Cookie;
use std::collections::HashMap;

fn cookie_decode(value: &str) -> HashMap<&str, &str> {
    Cookie::parse(value).unwrap()
}

The error I get, but seems obvious it is a different type.

= note: expected struct `HashMap<&str, &str>`
              found struct `Vec<Cookie<'_>>`

Is it possible to convert to another data structure or somehow ignore the struct type? I am only looking for key/value strings I can use elsewhere.

Any pointers much appreciated.

This seems trivial? Can't you just iterate over the cookies and call their get_name() and get_value() methods?

use basic_cookies::{Cookie, Error};
use std::collections::HashMap;

fn cookie_decode(value: &str) -> Result<HashMap<&str, &str>, Error> {
    Cookie::parse(value)
        .map(|cs| {
            cs.into_iter()
                .map(|c| (c.get_name(), c.get_value()))
                .collect()
        })
}
2 Likes

You can convert cookies to a HashMap, but you can't ignore the struct type and pretend Vec<Cookie<'_>> is a HashMap<&str, &str>. Both are completely different and distinct types.

Here a basic example using the cookie and itertools crates to iterate over the cookies in your string slice to construct a HashMap of name, value pairs from it:

use cookie::{Cookie, ParseError};
use itertools::Itertools;
use std::collections::HashMap;

pub fn cookie_decode(value: &str) -> Result<HashMap<&str, &str>, ParseError> {
    Cookie::split_parse(value) 
        .map_ok(|c| (c.name_raw().unwrap(), c.value_raw().unwrap()))
        .try_collect()
}

fn main() {
    let cookies = "name=value; other=key%20value";
    
    let cookies = cookie_decode(cookies).unwrap();
    
    assert_eq!(cookies["name"], "value");
    assert_eq!(cookies["other"], "key%20value");
}

Playground.

1 Like

It seems basic_cookies doesn't seem to allow for comma separated values as I was only getting the first part returned.

I have switched to use cookie following the second example. Do I need itertools or can I use "into_iter" and use the Rust provided one? I also went from "parse" to "split_parse" as I was only getting a single cookie name/value. Maybe parse would work but I was missing something.

I am getting an error with the value returned. The first was about ownership but using "to_owned" seems to fix that. The next was around str and String, but returning String seems to fix that.

fn cookie_decode(value: &str) -> HashMap<String, String> {
Cookie::split_parse(value).map(|cookies| {
        cookies.into_iter()
        .map(|cookie| (cookie.name().to_owned(), cookie.value().to_owned()))
        .collect()
    })
}

I am now getting this but not sure how I can return a HashMap without SplitCookies as a struct. Maybe I have to use itertools but thought there might be another way.

expected `HashMap<String, String>`, found `Map<SplitCookies<'_>, ...>`

You don't need itertools, I just used it for the convenient iterator combinators. Here a "vanilla" version without it:

use cookie::{Cookie, ParseError};
use std::collections::HashMap;

pub fn cookie_decode(value: &str) -> Result<HashMap<&str, &str>, ParseError> {
    let mut res = HashMap::new();
    
    for c in Cookie::split_parse(value) {
        let c = c?;
        
        res.insert(c.name_raw().unwrap(), c.value_raw().unwrap());
    }
    
    Ok(res)
}

fn main() {
    let cookies = "name=value; other=key%20value";

    let cookies = cookie_decode(cookies).unwrap();

    assert_eq!(cookies["name"], "value");
    assert_eq!(cookies["other"], "key%20value");
}

Playground.

1 Like

Thank you. I ended up making some small changes. I wanted to return an empty HashMap instead of an error, so used match.

fn cookie_decode(value: &str) -> HashMap<&str, &str> {
    let mut cookies = HashMap::new();

    for cookie in Cookie::split_parse(value) {
        match cookie {
            Ok(c) => {
                cookies.insert(c.name_raw().unwrap(), c.value_raw().unwrap());
            },
            _ => continue,
        };
    }

    cookies
}

I benchmarked this against my first version using basic_cookies and the basic_cookies does 27.29 K iterations/second but this recent approach using cookie does 55.30 K which is double the amount.