ultimate64/
drives.rs

1//! # Disk drive and disk image manipulation
2
3use crate::aux;
4use anyhow::{bail, Result};
5use clap::ValueEnum;
6use serde::{Deserialize, Serialize};
7use std::{collections::HashMap, fmt::Display, path::Path};
8
9/// Disk drive types
10#[derive(Serialize, Deserialize, Debug, Clone, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
11pub enum DriveType {
12    #[serde(rename = "1541")]
13    CBM1541,
14    #[serde(rename = "1571")]
15    CBM1571,
16    #[serde(rename = "1581")]
17    CBM1581,
18    #[serde(rename = "DOS emulation")]
19    DOS,
20}
21
22impl Display for DriveType {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        let s = match self {
25            Self::CBM1541 => "1541",
26            Self::CBM1571 => "1571",
27            Self::CBM1581 => "1581",
28            Self::DOS => "DOS emulation",
29        };
30        write!(f, "{s}")
31    }
32}
33
34/// Disk image types
35#[derive(Serialize, Deserialize, Debug, Clone, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
36pub enum DiskImageType {
37    #[clap(name = "d64")]
38    D64,
39    #[clap(name = "g64")]
40    G64,
41    #[clap(name = "d71")]
42    D71,
43    #[clap(name = "g71")]
44    G71,
45    #[clap(name = "d81")]
46    D81,
47}
48
49impl DiskImageType {
50    /// New disk image type from file name
51    pub fn from_file_name<T: AsRef<Path>>(path: T) -> Result<Self> {
52        let ext = aux::get_extension(path).unwrap_or_default();
53        Ok(match ext.as_str() {
54            "d64" => Self::D64,
55            "d71" => Self::D71,
56            "d81" => Self::D81,
57            "g64" => Self::G64,
58            "g71" => Self::G71,
59            _ => bail!("File extension must be one of: d64, g64, d71, g71, d81"),
60        })
61    }
62}
63
64impl Display for DiskImageType {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        let s = match self {
67            Self::D64 => "d64",
68            Self::G64 => "g64",
69            Self::D71 => "d71",
70            Self::G71 => "g71",
71            Self::D81 => "d81",
72        };
73        write!(f, "{s}")
74    }
75}
76
77/// Drive mount modes
78#[derive(Serialize, Deserialize, Debug, Clone, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
79pub enum MountMode {
80    /// Read and write access
81    #[clap(name = "rw")]
82    ReadWrite,
83    /// Read only access
84    #[clap(name = "ro")]
85    ReadOnly,
86    /// Unlinked
87    #[clap(name = "ul")]
88    Unlinked,
89}
90
91impl TryFrom<&str> for MountMode {
92    type Error = String;
93    fn try_from(s: &str) -> Result<Self, Self::Error> {
94        use MountMode::*;
95        match s {
96            "readwrite" => Ok(ReadWrite),
97            "readonly" => Ok(ReadOnly),
98            "unlinked" => Ok(Unlinked),
99            _ => Err(format!("Unknown mount mode: {s}")),
100        }
101    }
102}
103
104impl Display for MountMode {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        use MountMode::*;
107        let s = match self {
108            ReadWrite => "readwrite",
109            ReadOnly => "readonly",
110            Unlinked => "unlinked",
111        };
112        write!(f, "{s}")
113    }
114}
115
116/// Drive description
117#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
118pub struct Drive {
119    /// Bus ID
120    pub bus_id: u8,
121    /// Enabled
122    pub enabled: bool,
123    /// Type
124    #[serde(rename = "type")]
125    pub drive_type: Option<DriveType>,
126    /// Last error
127    pub last_error: Option<String>,
128    /// ROM
129    pub rom: Option<String>,
130    /// Image file
131    pub image_file: Option<String>,
132    /// Image path
133    pub image_path: Option<String>,
134}
135
136/// Structure to handle raw drive list from Ultimate
137#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
138pub struct DriveList {
139    /// List of drives
140    pub drives: Vec<HashMap<String, Drive>>,
141}