'Match and do the same function block except for a type

I need to do the same operations as in Some(Md5Sess), changing only the type that calls digest. So change from <md5::Md5>::digest to <sha2::Sha256>::digest etc and possibly many others:

match challenge.algorithm {
    Some(Md5Sess) => {
        //I need to repeat this entire block, only changing the `md5::Md5` 
        //by the correct hashing function
        let a1_part1 =
            <md5::Md5>::digest(format!("{}:{}:{}", username, realm, password).as_bytes());
        let cnonce = match cnonce {
            Some(cnonce) => cnonce,
            None => return Err(DigestError::MissingCNonce),
        };
        let a1_part2 = format!("{}:{}", nonce, cnonce);
        a1 = format!("{:x}:{}", a1_part1, a1_part2);
        let entity_body = match &body {
            Some(body) => body.as_ref(),
            None => "".as_bytes(),
        };
        hash_common::<md5::Md5>(
            method,
            chosen_qop,
            a1,
            nonce,
            Some(cnonce),
            nonce_count,
            uri,
            body,
        )
    }
    Some(Sha512Trunc256Sess) => 
        Err(DigestError::DigestAlgorithmNotImplemented(Sha512Trunc256Sess)),
    Some(Sha256Sess) => 
        Err(DigestError::DigestAlgorithmNotImplemented(Sha256Sess)),
    Some(Algorithm::Other(s)) => 
        Err(DigestError::DigestAlgorithmNotImplemented(Algorithm::Other(s))),
}

My only idea is to create a function that is generic on the type of the hash, with the inconvenient of having lots of arguments, for each variable used in that block.

Is there a better way to solve this problem?

I tried with macros too, only to remember that in Rust, macros are not like in C where they don't care about the text being used. I did a macro that matched by the type of the hash like my_macro!(md5::Md5) but then it complained about the variables being used in the block.



Solution 1:[1]

Disclaimer: I do not claim this is the best solution, as I would probably prefer using a function if at all possible, but if that is out of the question, here goes:

The duplicate can help you reuse your block in all the match cases:

use duplicate::duplicate:

duplicate! {
    [
        func(hash_type);
        [
            //I need to repeat this entire block, only changing the `md5::Md5` by the correct hashing function
            let a1_part1 = <hash_type>::digest(format!("{}:{}:{}", username, realm, password).as_bytes());
            let cnonce = match cnonce {
                Some(cnonce) => cnonce,
                None => return Err(DigestError::MissingCNonce)
            };
            let a1_part2 = format!("{}:{}", nonce, cnonce);
            a1 = format!("{:x}:{}", a1_part1, a1_part2);
            let entity_body = match &body {
                Some(body) => body.as_ref(),
                None => "".as_bytes()
            };
            hash_common::<hash_type>(method, chosen_qop, a1, nonce, Some(cnonce), nonce_count, uri, body)
        ];
    ]
    match challenge.algorithm {
        Some(Md5Sess) => {
            func([md5::Md5]) // Duplicates the block with hash_type = md5::Md5
        },
        Some(Sha512Trunc256Sess) => {
            func([sha2::Sha256])// Duplicates the block with hash_type = sha2::Sha256
        },
        ...
    }
}

This will repeat the function body (which has been out of the its original block and into a substitution) in each match case, replacing hash_type with the hash type you provide in func([md5::Md5]), func([sha2::Sha256]), etc.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1