mcp4x 1.0.0

Platform-agnostic Rust driver for the MCP4x digital potentiometers.
Documentation
use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTrans};
use mcp4x::{ic, interface, Channel, Error, Mcp4x};

macro_rules! device_support {
    ($create:ident, $destroy:ident, $ic:ident) => {
        pub fn $create(
            transactions: &[SpiTrans<u8>],
        ) -> Mcp4x<interface::SpiInterface<SpiMock<u8>>, ic::$ic> {
            let wrapped: Vec<SpiTrans<u8>> = transactions
                .iter()
                .flat_map(|trans| {
                    [
                        SpiTrans::transaction_start(),
                        trans.clone(),
                        SpiTrans::transaction_end(),
                    ]
                })
                .collect();
            Mcp4x::$create(SpiMock::new(&wrapped))
        }

        pub fn $destroy(dev: Mcp4x<interface::SpiInterface<SpiMock<u8>>, ic::$ic>) {
            dev.$destroy().done();
        }
    };
}

device_support!(new_mcp41x, destroy_mcp41x, Mcp41x);
device_support!(new_mcp42x, destroy_mcp42x, Mcp42x);

#[macro_export]
macro_rules! test {
    ($name:ident, $method:ident, $create:ident, $destroy:ident,
        $cmd:expr, $expected_value:expr, $( $arg:expr ),* ) => {
        #[test]
        fn $name() {
            let trans = [SpiTrans::write_vec(vec![$cmd, $expected_value])];
            let mut dev = $create(&trans);
            dev.$method($($arg),*).unwrap();
            $destroy(dev);
        }
    };
}

mod mcp41x {
    use super::*;

    test!(
        set_position_ch0,
        set_position,
        new_mcp41x,
        destroy_mcp41x,
        0b0001_0001,
        50,
        Channel::Ch0,
        50
    );
    test!(
        set_position_all,
        set_position,
        new_mcp41x,
        destroy_mcp41x,
        0b0001_0011,
        50,
        Channel::All,
        50
    );

    test!(
        shutdown_ch0,
        shutdown,
        new_mcp41x,
        destroy_mcp41x,
        0b0010_0001,
        0,
        Channel::Ch0
    );
    test!(
        shutdown_all,
        shutdown,
        new_mcp41x,
        destroy_mcp41x,
        0b0010_0011,
        0,
        Channel::All
    );

    fn assert_wrong_channel<T, CommE>(result: &Result<T, Error<CommE>>) {
        match result {
            Err(Error::WrongChannel) => (),
            _ => panic!("Wrong channel not reported."),
        }
    }

    #[test]
    fn wrong_channel_matches() {
        assert_wrong_channel::<(), ()>(&Err(Error::WrongChannel));
    }

    #[should_panic]
    #[test]
    fn wrong_channel_can_fail() {
        assert_wrong_channel::<(), ()>(&Ok(()));
    }

    #[test]
    fn shutdown_cannot_provide_invalid_channel_ch1() {
        let mut dev = new_mcp41x(&[]);
        assert_wrong_channel(&dev.shutdown(Channel::Ch1));
        dev.destroy_mcp41x().done();
    }

    #[test]
    fn set_position_cannot_provide_invalid_channel() {
        let mut dev = new_mcp41x(&[]);
        assert_wrong_channel(&dev.set_position(Channel::Ch1, 0));
        dev.destroy_mcp41x().done();
    }
}

mod mcp42x {
    use super::*;

    test!(
        set_position_ch0,
        set_position,
        new_mcp42x,
        destroy_mcp42x,
        0b0001_0001,
        50,
        Channel::Ch0,
        50
    );

    test!(
        set_position_ch1,
        set_position,
        new_mcp42x,
        destroy_mcp42x,
        0b0001_0010,
        50,
        Channel::Ch1,
        50
    );

    test!(
        set_position_all,
        set_position,
        new_mcp42x,
        destroy_mcp42x,
        0b0001_0011,
        50,
        Channel::All,
        50
    );

    test!(
        shutdown_ch0,
        shutdown,
        new_mcp42x,
        destroy_mcp42x,
        0b0010_0001,
        0,
        Channel::Ch0
    );

    test!(
        shutdown_ch1,
        shutdown,
        new_mcp42x,
        destroy_mcp42x,
        0b0010_0010,
        0,
        Channel::Ch1
    );

    test!(
        shutdown_all,
        shutdown,
        new_mcp42x,
        destroy_mcp42x,
        0b0010_0011,
        0,
        Channel::All
    );
}