HashMap<K,V> multibinding

Similar to Vec<T> multibinding, a #[provides] or #[binds] binding can also be marked with the #[into_map] attribute, which collects the key-value pair into a HashMap

While a map can be created by multibinding Vec<(K,V)> or some other entry generating mechanisms, the HashMap<K,V> multibinding has additional compile time checks to make sure there are no key collisions.

Map keys

The value type is specified by the binding method return value, but the key type and value needs to be specified by a metadata in the #[into_map] attribute.

string_key

string_key specifies the map key is a String. The value must be a string literal, lockjaw is unable to resolve more complex compile time constants.

This example binds to HashMap<String,String>:

    #[provides]
    #[into_map(string_key: "1")]
    pub fn provide_string1() -> String {
        "string1".to_owned()
    }

    #[provides]
    #[into_map(string_key: "2")]
    pub fn provide_string2() -> String {
        "string2".to_owned()
    }

i32_key

i32_key specifies the map key is an i32. The value must be an i32 literal, lockjaw is unable to resolve more complex compile time constants.

This example binds to HashMap<i32,String>:

    #[provides]
    #[into_map(i32_key: 1)]
    pub fn provide_i32_string1() -> String {
        "string1".to_owned()
    }

    #[provides]
    #[into_map(i32_key: 2)]
    pub fn provide_i32_string2() -> String {
        "string2".to_owned()
    }

Other types are not implemented. i32 ought to be enough for everyone.

enum_key

i32_key specifies the map key is an enum. Since the enum is going to be used as the map key, it must satisfy the same constraints HashMap gives, which is implementing Eq and Hash. It also must be a simple enum with no fields so Lockjaw knows how to compare them at compile time (meaning comparing the name is enough).

#[derive(Eq, PartialEq, Hash)]
pub enum E {
    Foo,
    Bar,
}

This example binds to HashMap<E,String>:

    #[provides]
    #[into_map(enum_key: E::Foo)]
    pub fn provide_enum_string1() -> String {
        "string1".to_owned()
    }

    #[provides]
    #[into_map(enum_key: Bar)]
    pub fn provide_enum_string2() -> String {
        "string2".to_owned()
    }

Lockjaw is able to infer the enum type (E) if the value is imported (use E::Bar), but the code maybe be more readable if the type is explicitly spelled out, especially most IDEs today cannot properly inspect tokens inside the metadata.

Qualifiers

#[into_map] can also be #[qualified]

    #[provides]
    #[qualified(Q)]
    #[into_map(string_key: "1")]
    pub fn provide_q_string1() -> String {
        "q_string1".to_owned()
    }

Which result in #[qualified(Q)] HashMap<String, String>. Note that the container is qualified instead of the content.

Dynamic map entries

All bindings in #[into_map] must be resolved at compile time, There are no #[elements_into_vec] equivalent such as #[elements_into_map].

However dynamic map entries can be achieved by rebinding Vec<(K,V)> into a HashMap<K,V>.

Examples

https://github.com/azureblaze/lockjaw/blob/main/tests/module_provides_into_map.rs