cd_da_reader/
discovery.rs1use crate::{CdReader, CdReaderError};
2
3#[derive(Debug, Clone)]
10pub struct DriveInfo {
11 pub path: String,
14 pub has_audio_cd: bool,
16}
17
18impl CdReader {
19 pub fn list_drives() -> Result<Vec<DriveInfo>, CdReaderError> {
27 let mut paths = crate::platform::list_drive_paths()?;
28 paths.sort();
29 paths.dedup();
30
31 let mut drives = Vec::with_capacity(paths.len());
32 for path in paths {
33 let has_audio_cd = match Self::open_path(&path) {
34 Ok(reader) => match reader.read_toc() {
35 Ok(toc) => toc.tracks.iter().any(|track| track.is_audio),
36 Err(_) => false,
37 },
38 Err(_) => false,
39 };
40
41 drives.push(DriveInfo { path, has_audio_cd });
42 }
43
44 Ok(drives)
45 }
46
47 pub fn open_default() -> Result<Self, CdReaderError> {
56 let drives = Self::list_drives()?;
57 let chosen = pick_default_drive(&drives).ok_or(CdReaderError::NoUsableDrive)?;
58
59 Self::open(chosen)
60 }
61}
62
63fn pick_default_drive(drives: &[DriveInfo]) -> Option<&DriveInfo> {
64 drives.iter().find(|drive| drive.has_audio_cd)
65}
66
67#[cfg(test)]
68mod tests {
69 use super::{DriveInfo, pick_default_drive};
70
71 #[test]
72 fn chooses_first_audio_drive() {
73 let drives = vec![
74 DriveInfo {
75 path: "disk10".to_string(),
76 has_audio_cd: false,
77 },
78 DriveInfo {
79 path: "disk11".to_string(),
80 has_audio_cd: true,
81 },
82 DriveInfo {
83 path: "disk12".to_string(),
84 has_audio_cd: true,
85 },
86 ];
87
88 assert_eq!(
89 pick_default_drive(&drives).map(|drive| drive.path.as_str()),
90 Some("disk11")
91 );
92 }
93
94 #[test]
95 fn returns_none_when_no_audio_drive() {
96 let drives = vec![DriveInfo {
97 path: "/dev/sr0".to_string(),
98 has_audio_cd: false,
99 }];
100
101 assert!(pick_default_drive(&drives).is_none());
102 }
103}