Сопоставьте и выполните один и тот же функциональный блок, за исключением типа

#rust #repeat

Вопрос:

Мне нужно выполнить те же операции , что и в Some(Md5Sess) , изменив только тип, который вызывает digest . Поэтому измените с <md5::Md5>::digest на <sha2::Sha256>::digest и т. Д. и, возможно, многие другие:

 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 amp;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))),
}
 

Моя единственная идея состоит в том, чтобы создать функцию, которая является универсальной по типу хэша, с неудобством наличия большого количества аргументов для каждой переменной, используемой в этом блоке.

Есть ли лучший способ решить эту проблему?

Я тоже пробовал работать с макросами, только чтобы помнить, что в Rust макросы не похожи на C, где им наплевать на используемый текст. Я сделал макрос, который соответствовал типу хэша, например my_macro!(md5::Md5), но затем он пожаловался на переменные, используемые в блоке.

Комментарии:

1. Что такое hash_common и почему он принимает алгоритм в качестве параметра типа?

2. @LambdaFairy это еще одна функция, которая выполняет некоторые общие функции, используемые всеми блоками. Я вижу, что вы думаете, что я могу просто поместить свой блок внутри функции hash_function, но это невозможно. Этот блок предназначен только для HashSess случаев совпадения. Я скрыл случаи, не связанные с sess, потому что они просто звонят hash_common напрямую

Ответ №1:

Возможно, вы сможете сделать это, сначала выбрав функции для применения, а затем манипулируя данными:

 // Select the functions to apply
let (digest, common) = match challenge.algorithm {
    Some (Md5Sess) => (<md5::Md5>::digest, hash_common::<md5::Md5>),
    Some (Sha256Sess) => (<sha2::Sha256>::digest, hash_common::<sha2::Sha256>),
    _ => todo!(),
}

// Now process the data
let a1_part1 = 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 amp;body {
    Some (body) => body.as_ref(),
    None => "".as_bytes()
};
common (method, chosen_qop, a1, nonce, Some (cnonce), nonce_count, uri, body)
 

Комментарии:

1. это хороший ifea. К сожалению, я спрятал несколько дел. Есть HashSess случаи, когда применяется этот блок кода, но есть Hash случаи, для которых блок не применяется. Знаете ли вы элегантное решение для применения блока HashSess и другого блока Hash ?

2. Если вы хотите включить или отключить части обработки, вы можете использовать функцию возврата соответствия Option вместо прямой, чтобы затем if let применять или не применять каждую часть.

3. К сожалению, это не работает: Some (Sha256Sess) => (<sha2::Sha256>::digest, hash_common::<sha2::Sha256>), | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct md5::Md5 , found struct sha2::Sha2`. Полная ошибка: pastebin.com/TryKVucK

4. Выходные fn данные имеют внутренний тип, называемый Output . Этот внутренний тип изменяется в соответствии с хэш-функцией. Таким образом, эти функции не могут быть все одного типа, как того требует match

5. Затем вы можете обернуть функцию в замыкание, которое унифицирует тип возвращаемого значения: |s| <md5::Md5>::digest (s).as_bytes() .

Ответ №2:

Отказ от ответственности: Я не утверждаю, что это лучшее решение, так как я, вероятно, предпочел бы использовать функцию, если это вообще возможно, но если об этом не может быть и речи, вот:

Дубликат может помочь вам повторно использовать ваш блок во всех случаях совпадения:

 use duplicate::duplicate_inline:

duplicate_inline! {
    [
        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 amp;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
        },
        ...
    }
}
 

Это повторит тело функции (которое было выведено из исходного блока и заменено) в каждом случае совпадения, hash_type заменяя его типом хэша, который вы указываете в func([md5::Md5]) func([sha2::Sha256]) , и т.д.