RustCrypto symmetric cipher wrapper

Hi, I am trying to switch from openssl to RustCrypto block ciphers DES, 3DES and AES. Currently, I have many functions for each pairing, i.e. 3DES-CBC-Encrypt, 3DES-CBC-Decrypt, 3DES-ECB-Encrypt ... but I would like to get a wrapper function but I have so far been unsuccessful to use the traits provided by the RustCrypto cipher crate. Could anyone help me figure it out? Currently, I have:
Cargo.toml:

[dependencies]
cipher = { version = "0.4.4", features = ["block-padding", "alloc"] }
des = "0.8.1"
ecb = "0.1.2"
cbc = "0.1.2"
use cipher::{BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyInit, KeyIvInit, KeySizeUser};

fn des3_cbc_encrypt(key: &[u8], data: &[u8]) -> Result<Vec<u8>, EmrtdError> {
    if key.len() != des::TdesEde2::key_size() {
        return Err(EmrtdError::InvalidArgument(
            "Wrong key size for 3DES encryption",
        ));
    }
    if data.len() % des::TdesEde2::block_size() != 0 {
        return Err(EmrtdError::InvalidArgument(
            "Wrong data size for 3DES encryption",
        ));
    }

    Ok(
        cbc::Encryptor::<des::TdesEde2>::new(key.into(), &Default::default())
            .encrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data),
    )
}

fn des3_cbc_decrypt(key: &[u8], data: &[u8]) -> Result<Vec<u8>, EmrtdError> {
    if key.len() != des::TdesEde3::key_size() {
        return Err(EmrtdError::InvalidArgument(
            "Wrong key size for 3DES decryption",
        ));
    }
    if data.len() % des::TdesEde3::block_size() != 0 {
        return Err(EmrtdError::InvalidArgument(
            "Wrong data size for 3DES decryption",
        ));
    }

    cbc::Decryptor::<des::TdesEde3>::new(key.into(), &Default::default())
        .decrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data)
        .map_err(EmrtdError::UnpadError)
}

fn des_ecb_encrypt(key: &[u8], data: &[u8]) -> Result<Vec<u8>, EmrtdError> {
    if key.len() != des::Des::key_size() {
        return Err(EmrtdError::InvalidArgument(
            "Wrong key size for DES encryption",
        ));
    }
    if data.len() % des::Des::block_size() != 0 {
        return Err(EmrtdError::InvalidArgument(
            "Wrong data size for DES encryption",
        ));
    }

    Ok(ecb::Encryptor::<des::Des>::new(key.into())
        .encrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data))
}

fn des_ecb_decrypt(key: &[u8], data: &[u8]) -> Result<Vec<u8>, EmrtdError> {
    if key.len() != des::Des::key_size() {
        return Err(EmrtdError::InvalidArgument(
            "Wrong key size for DES decryption",
        ));
    }
    if data.len() % des::Des::block_size() != 0 {
        return Err(EmrtdError::InvalidArgument(
            "Wrong data size for DES decryption",
        ));
    }
    
    ecb::Decryptor::<des::Des>::new(key.into())
        .decrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data)
        .map_err(EmrtdError::UnpadError)
}

And I would like to get a wrapper similar to what openssl has:

fn cipher(
    cipher: Cipher, 			// DES, AES128, AES256 etc.
    cipher_mode: CipherMode,	// CBC, ECB etc.
    mode: Mode, 				// Encrypt/Decrypt
    key: &[u8],					// Key
    iv: Option<&[u8]>,			// IV
    data: &[u8],				// Data
) -> Result<Vec<u8>, MyCustomError> {
	// TODO
}

I know that RustCrypto is trying to make traits and have each symmetric block cipher implement these traits, but I couldn't figure out how to write the function. I appreciate any help.

I came up with a solution like this, but I can't generalize ecb with cbc because ecb does not implement KeyIvInit:

fn encrypt<CM>(key: &[u8], data: &[u8], iv: Option<&[u8]>) -> Result<Vec<u8>, MyCustomError> where
CM: BlockEncryptMut + KeyIvInit
 {
    if key.len() != CM::key_size() {
        return Err(MyCustomError::InvalidArgument(
            "Wrong key size for cipher encryption",
        ));
    }
    if data.len() % CM::block_size() != 0 {
        return Err(MyCustomError::InvalidArgument(
            "Wrong data size for cipher encryption",
        ));
    }

    Ok(
        CM::new(key.into(), iv.unwrap_or_default().into())
            .encrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data),
    )
}

fn encrypt_ecb<CM>(key: &[u8], data: &[u8]) -> Result<Vec<u8>, MyCustomError> where
CM: BlockEncryptMut + KeyInit
 {
    if key.len() != CM::key_size() {
        return Err(MyCustomError::InvalidArgument(
            "Wrong key size for cipher encryption",
        ));
    }
    if data.len() % CM::block_size() != 0 {
        return Err(MyCustomError::InvalidArgument(
            "Wrong data size for cipher encryption",
        ));
    }

    Ok(CM::new(key.into()).encrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data))
}

fn decrypt<CM>(key: &[u8], data: &[u8], iv: Option<&[u8]>) -> Result<Vec<u8>, MyCustomError> where
CM: BlockDecryptMut + KeyIvInit
 {
    if key.len() != CM::key_size() {
        return Err(MyCustomError::InvalidArgument(
            "Wrong key size for cipher encryption",
        ));
    }
    if data.len() % CM::block_size() != 0 {
        return Err(MyCustomError::InvalidArgument(
            "Wrong data size for cipher encryption",
        ));
    }

    CM::new(key.into(), iv.unwrap_or_default().into())
        .decrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data)
        .map_err(MyCustomError::UnpadError)
}

fn decrypt_ecb<CM>(key: &[u8], data: &[u8]) -> Result<Vec<u8>, MyCustomError> where
CM: BlockDecryptMut + KeyInit
 {
    if key.len() != CM::key_size() {
        return Err(MyCustomError::InvalidArgument(
            "Wrong key size for cipher encryption",
        ));
    }
    if data.len() % CM::block_size() != 0 {
        return Err(MyCustomError::InvalidArgument(
            "Wrong data size for cipher encryption",
        ));
    }

    CM::new(key.into())
        .decrypt_padded_vec_mut::<cipher::block_padding::NoPadding>(data)
        .map_err(MyCustomError::UnpadError)
}

And instead of many functions, I can use 4 functions. But I still don't know how I could combine all of these into one.