use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Api {
Unspecified,
CoreMidi,
Alsa,
Jack,
WindowsMm,
WindowsUwp,
WebMidi,
AndroidAmidi,
Dummy,
}
impl Api {
pub fn name(&self) -> &'static str {
match self {
Api::Unspecified => "Unspecified",
Api::CoreMidi => "macOS CoreMIDI",
Api::Alsa => "Linux ALSA",
Api::Jack => "JACK Audio Connection Kit",
Api::WindowsMm => "Windows MultiMedia",
Api::WindowsUwp => "Windows UWP",
Api::WebMidi => "Web MIDI",
Api::AndroidAmidi => "Android AMidi",
Api::Dummy => "Dummy",
}
}
pub fn id(&self) -> &'static str {
match self {
Api::Unspecified => "unspecified",
Api::CoreMidi => "core",
Api::Alsa => "alsa",
Api::Jack => "jack",
Api::WindowsMm => "windows_ms",
Api::WindowsUwp => "windows_uwp",
Api::WebMidi => "web_midi",
Api::AndroidAmidi => "android",
Api::Dummy => "dummy",
}
}
pub fn from_id(id: &str) -> Option<Api> {
match id {
"unspecified" => Some(Api::Unspecified),
"core" => Some(Api::CoreMidi),
"alsa" => Some(Api::Alsa),
"jack" => Some(Api::Jack),
"windows_ms" => Some(Api::WindowsMm),
"windows_uwp" => Some(Api::WindowsUwp),
"web_midi" => Some(Api::WebMidi),
"android" => Some(Api::AndroidAmidi),
"dummy" => Some(Api::Dummy),
_ => None,
}
}
pub fn default_for_platform() -> Api {
#[cfg(target_os = "macos")]
return Api::CoreMidi;
#[cfg(target_os = "linux")]
return Api::Alsa;
#[cfg(target_os = "windows")]
return Api::WindowsMm;
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
return Api::Dummy;
}
pub fn available() -> Vec<Api> {
let mut apis = Vec::new();
#[cfg(target_os = "macos")]
apis.push(Api::CoreMidi);
#[cfg(target_os = "linux")]
{
apis.push(Api::Alsa);
}
#[cfg(target_os = "windows")]
apis.push(Api::WindowsMm);
if apis.is_empty() {
apis.push(Api::Dummy);
}
apis
}
}
impl Default for Api {
fn default() -> Self {
Api::default_for_platform()
}
}
impl fmt::Display for Api {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MidiPort {
index: usize,
name: String,
api: Api,
}
impl MidiPort {
pub fn new(index: usize, name: impl Into<String>, api: Api) -> Self {
Self {
index,
name: name.into(),
api,
}
}
pub fn index(&self) -> usize {
self.index
}
pub fn name(&self) -> &str {
&self.name
}
pub fn api(&self) -> Api {
self.api
}
}
impl fmt::Display for MidiPort {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.index, self.name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_default() {
let api = Api::default();
assert!(Api::available().contains(&api) || api == Api::Dummy);
}
#[test]
fn test_port_creation() {
let port = MidiPort::new(0, "Test Port", Api::Dummy);
assert_eq!(port.index(), 0);
assert_eq!(port.name(), "Test Port");
}
#[test]
fn test_api_id_roundtrip() {
for api in [
Api::Unspecified,
Api::CoreMidi,
Api::Alsa,
Api::Jack,
Api::WindowsMm,
Api::WindowsUwp,
Api::WebMidi,
Api::AndroidAmidi,
Api::Dummy,
] {
assert_eq!(Api::from_id(api.id()), Some(api));
}
assert_eq!(Api::from_id("not-a-real-api"), None);
}
#[test]
fn test_api_id_stable_and_name_display() {
assert_eq!(Api::CoreMidi.id(), "core");
assert_eq!(Api::CoreMidi.name(), "macOS CoreMIDI");
}
}