can-utils-rs 0.4.0

A pure Rust CLI utility for managing and manipulating CAN interfaces and sending or printing pretty CAN data.
Documentation
use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanBitrate {
    B10K = 10_000,
    B20K = 20_000,
    B50K = 50_000,
    B100K = 100_000,
    B125K = 125_000,
    B250K = 250_000,
    B500K = 500_000,
    B1M = 1_000_000,
}

impl fmt::Display for CanBitrate {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let label = match self {
            CanBitrate::B1M => "1 Mbit".to_string(),
            CanBitrate::B500K => "500 kbit".to_string(),
            CanBitrate::B250K => "250 kbit".to_string(),
            CanBitrate::B125K => "125 kbit".to_string(),
            CanBitrate::B100K => "100 kbit".to_string(),
            CanBitrate::B50K => "50 kbit".to_string(),
            CanBitrate::B20K => "20 kbit".to_string(),
            CanBitrate::B10K => "10 kbit".to_string(),
        };

        write!(f, "{} ({})", label, *self as u32)
    }
}

impl CanBitrate {
    pub fn bitrate(&self) -> u32 {
        *self as u32
    }
    pub fn can_bitrates() -> Vec<CanBitrate> {
        let mut bitrates = vec![
            CanBitrate::B10K,
            CanBitrate::B20K,
            CanBitrate::B50K,
            CanBitrate::B100K,
            CanBitrate::B125K,
            CanBitrate::B250K,
            CanBitrate::B500K,
            CanBitrate::B1M,
        ];

        bitrates.sort_by_key(|b| std::cmp::Reverse(*b as u32));
        bitrates
    }
}

#[derive(Debug, Clone)]
pub struct SlcanSpeed {
    pub bitrate: u32,
    pub flag: &'static str,
}

impl fmt::Display for SlcanSpeed {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let label = match self.bitrate {
            1_000_000 => "1 Mbit".to_string(),
            _ if self.bitrate >= 1_000 => format!("{} kbit", self.bitrate / 1_000),
            _ => self.bitrate.to_string(),
        };

        write!(f, "{:<10} ({}, {})", label, self.bitrate, self.flag)
    }
}

pub fn slcan_speeds() -> Vec<SlcanSpeed> {
    let mut speeds = vec![
        SlcanSpeed {
            bitrate: 10_000,
            flag: "s0",
        },
        SlcanSpeed {
            bitrate: 20_000,
            flag: "s1",
        },
        SlcanSpeed {
            bitrate: 50_000,
            flag: "s2",
        },
        SlcanSpeed {
            bitrate: 100_000,
            flag: "s3",
        },
        SlcanSpeed {
            bitrate: 125_000,
            flag: "s4",
        },
        SlcanSpeed {
            bitrate: 250_000,
            flag: "s5",
        },
        SlcanSpeed {
            bitrate: 500_000,
            flag: "s6",
        },
        SlcanSpeed {
            bitrate: 800_000,
            flag: "s7",
        },
        SlcanSpeed {
            bitrate: 1_000_000,
            flag: "s8",
        },
    ];

    speeds.sort_by(|a, b| b.bitrate.cmp(&a.bitrate));
    speeds
}

#[derive(Debug, Clone)]
pub enum CanMode {
    Native,
    Slcan,
    Virtual,
}

impl fmt::Display for CanMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CanMode::Native => write!(f, "Native CAN bus"),
            CanMode::Slcan => write!(f, "Non-native CAN bus (slcand)"),
            CanMode::Virtual => write!(f, "Virtual CAN bus (vcan)"),
        }
    }
}

#[derive(Debug, Clone)]
pub struct NativeConfig {
    pub iface: String,
    pub bitrate: CanBitrate,
}

impl NativeConfig {
    pub fn new(iface: String, bitrate: CanBitrate) -> Self {
        Self { iface, bitrate }
    }
}

#[derive(Debug, Clone)]
pub struct SlcanConfig {
    pub tty: String,
    pub iface: String,
    pub speed: SlcanSpeed,
    pub uart_baud: u32,
}

impl SlcanConfig {
    pub fn new(tty: String, iface: String, speed: SlcanSpeed, uart_baud: u32) -> Self {
        Self {
            tty,
            iface,
            speed,
            uart_baud,
        }
    }
}

#[derive(Debug, Clone)]
pub struct VirtualConfig {
    pub iface: String,
}

impl VirtualConfig {
    pub fn new(iface: String) -> Self {
        Self { iface }
    }
}

#[derive(Debug, Clone)]
pub enum CanConfig {
    Native(NativeConfig),
    Slcan(SlcanConfig),
    Virtual(VirtualConfig),
}

impl CanConfig {
    pub fn new(
        mode: CanMode,
        iface: String,
        bitrate: Option<CanBitrate>,
        slcan_speed: Option<SlcanSpeed>,
        uart_baud: Option<u32>,
    ) -> Self {
        match mode {
            CanMode::Native => CanConfig::Native(NativeConfig {
                iface,
                bitrate: bitrate.expect("Bitrate is required for Native mode"),
            }),
            CanMode::Slcan => CanConfig::Slcan(SlcanConfig {
                tty: "/dev/ttyUSB0".to_string(), // Default value, can be modified later
                iface,
                speed: slcan_speed.expect("SLCAN speed is required for Slcan mode"),
                uart_baud: uart_baud.expect("UART baud rate is required for Slcan mode"),
            }),
            CanMode::Virtual => CanConfig::Virtual(VirtualConfig { iface }),
        }
    }
    pub fn iface(&self) -> &str {
        match self {
            CanConfig::Native(cfg) => &cfg.iface,
            CanConfig::Slcan(cfg) => &cfg.iface,
            CanConfig::Virtual(cfg) => &cfg.iface,
        }
    }

    pub fn set_iface(&mut self, new_iface: String) {
        match self {
            CanConfig::Native(cfg) => cfg.iface = new_iface,
            CanConfig::Slcan(cfg) => cfg.iface = new_iface,
            CanConfig::Virtual(cfg) => cfg.iface = new_iface,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExistingIfaceAction {
    Replace,
    Rename,
    Skip,
    Cancel,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterfaceResolution {
    Proceed,
    SkipSetup,
}