midi-io 0.1.1

Stream and send strictly-typed MIDI messages
Documentation
#[cfg(target_os = "linux")]
use std::ffi::CStr;
use std::ffi::CString;

use crate::NameError;

#[derive(Clone, Debug)]
pub(crate) struct Name(CString);

impl TryFrom<&str> for Name {
    type Error = NameError;

    fn try_from(name: &str) -> Result<Self, Self::Error> {
        Ok(Self(CString::new(name)?))
    }
}

impl Name {
    pub(crate) fn as_str(&self) -> &str {
        self.0
            .to_str()
            .expect("Name is only constructed from &str, so it is valid UTF-8")
    }

    #[cfg(target_os = "linux")]
    pub(crate) fn as_c_str(&self) -> &CStr {
        &self.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn try_from_rejects_interior_nul() {
        assert!(matches!(
            Name::try_from("bad\0name"),
            Err(NameError::ContainsNul(_))
        ));
    }

    #[test]
    fn try_from_rejects_leading_and_trailing_nul() {
        assert!(Name::try_from("\0name").is_err());
        assert!(Name::try_from("name\0").is_err());
    }

    #[test]
    fn as_str_round_trips() {
        let name = Name::try_from("midi-io client").expect("valid name");
        assert_eq!(name.as_str(), "midi-io client");
    }

    #[test]
    fn as_str_round_trips_non_ascii_utf8() {
        let name = Name::try_from("sinté 🎹").expect("valid name");
        assert_eq!(name.as_str(), "sinté 🎹");
    }

    #[cfg(target_os = "linux")]
    #[test]
    fn as_c_str_is_nul_terminated_original() {
        let name = Name::try_from("alsa client").expect("valid name");
        assert_eq!(name.as_c_str(), c"alsa client");
    }
}