Skip to content

Commit 0326002

Browse files
committed
disks: Eliminate all dispatch in favour of Deref
We've split disk into its own file now and forwarded on all useful methods while still keeping NVME/SCSI types detectable such that we can expand on them in future for any special properties. Signed-off-by: Ikey Doherty <[email protected]>
1 parent 3c7a7f2 commit 0326002

File tree

2 files changed

+158
-151
lines changed

2 files changed

+158
-151
lines changed

crates/disks/src/disk.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use core::fmt;
6+
use std::{
7+
fs,
8+
ops::Deref,
9+
path::{Path, PathBuf},
10+
};
11+
12+
use crate::{nvme, partition::Partition, scsi, sysfs, DEVFS_DIR};
13+
14+
/// Represents the type of disk device.
15+
#[derive(Debug)]
16+
pub enum Disk {
17+
/// SCSI disk device (e.g. sda, sdb)
18+
Scsi(scsi::Disk),
19+
/// NVMe disk device (e.g. nvme0n1)
20+
Nvme(nvme::Disk),
21+
}
22+
23+
impl Deref for Disk {
24+
type Target = BasicDisk;
25+
26+
// Let scsi and nvme disks deref to BasicDisk
27+
fn deref(&self) -> &Self::Target {
28+
match self {
29+
Disk::Scsi(disk) => disk,
30+
Disk::Nvme(disk) => disk,
31+
}
32+
}
33+
}
34+
35+
/// A basic disk representation containing common attributes shared by all disk types.
36+
/// This serves as the base structure that specific disk implementations build upon.
37+
#[derive(Debug)]
38+
pub struct BasicDisk {
39+
/// Device name (e.g. sda, nvme0n1)
40+
name: String,
41+
/// Total number of sectors on the disk
42+
sectors: u64,
43+
/// Path to the device in /dev
44+
device: PathBuf,
45+
/// Optional disk model name
46+
model: Option<String>,
47+
/// Optional disk vendor name
48+
vendor: Option<String>,
49+
/// Partitions
50+
partitions: Vec<Partition>,
51+
}
52+
53+
impl fmt::Display for Disk {
54+
// forward Display to BasicDisk
55+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56+
self.deref().fmt(f)
57+
}
58+
}
59+
60+
impl fmt::Display for BasicDisk {
61+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62+
let bytes = self.size();
63+
let gib = bytes as f64 / 1_073_741_824.0;
64+
65+
write!(f, "{} ({:.2} GiB)", self.name(), gib)?;
66+
67+
if let Some(vendor) = self.vendor() {
68+
write!(f, " - {}", vendor)?;
69+
}
70+
71+
if let Some(model) = self.model() {
72+
write!(f, " {}", model)?;
73+
}
74+
75+
Ok(())
76+
}
77+
}
78+
79+
impl BasicDisk {
80+
/// Returns the name of the disk device.
81+
pub fn name(&self) -> &str {
82+
&self.name
83+
}
84+
85+
/// Returns the partitions on the disk.
86+
pub fn partitions(&self) -> &[Partition] {
87+
&self.partitions
88+
}
89+
90+
/// Returns the path to the disk device in dev.
91+
pub fn device_path(&self) -> &Path {
92+
&self.device
93+
}
94+
95+
/// Returns the total number of sectors on the disk.
96+
pub fn sectors(&self) -> u64 {
97+
self.sectors
98+
}
99+
100+
/// Returns the size of the disk in bytes.
101+
pub fn size(&self) -> u64 {
102+
self.sectors() * 512
103+
}
104+
105+
/// Returns the model name of the disk.
106+
pub fn model(&self) -> Option<&str> {
107+
self.model.as_deref()
108+
}
109+
110+
/// Returns the vendor name of the disk.
111+
pub fn vendor(&self) -> Option<&str> {
112+
self.vendor.as_deref()
113+
}
114+
}
115+
116+
/// Trait for initializing different types of disk devices from sysfs.
117+
pub(crate) trait DiskInit: Sized {
118+
/// Creates a new disk instance by reading information from the specified sysfs path.
119+
///
120+
/// # Arguments
121+
///
122+
/// * `root` - The root sysfs directory path
123+
/// * `name` - The name of the disk device
124+
///
125+
/// # Returns
126+
///
127+
/// `Some(Self)` if the disk was successfully initialized, `None` otherwise
128+
fn from_sysfs_path(root: &Path, name: &str) -> Option<Self>;
129+
}
130+
131+
impl DiskInit for BasicDisk {
132+
fn from_sysfs_path(sysroot: &Path, name: &str) -> Option<Self> {
133+
let node = sysroot.join(name);
134+
135+
// Read the partitions of the disk if any
136+
let mut partitions: Vec<_> = fs::read_dir(&node)
137+
.ok()?
138+
.filter_map(Result::ok)
139+
.filter_map(|e| {
140+
let name = e.file_name().to_string_lossy().to_string();
141+
Partition::from_sysfs_path(sysroot, &name)
142+
})
143+
.collect();
144+
partitions.sort_by_key(|p| p.number);
145+
146+
Some(Self {
147+
name: name.to_owned(),
148+
sectors: sysfs::sysfs_read(sysroot, &node, "size").unwrap_or(0),
149+
device: PathBuf::from(DEVFS_DIR).join(name),
150+
model: sysfs::sysfs_read(sysroot, &node, "device/model"),
151+
vendor: sysfs::sysfs_read(sysroot, &node, "device/vendor"),
152+
partitions,
153+
})
154+
}
155+
}

crates/disks/src/lib.rs

Lines changed: 3 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22
//
33
// SPDX-License-Identifier: MPL-2.0
44

5-
use std::{
6-
fmt, fs, io,
7-
path::{Path, PathBuf},
8-
};
9-
10-
use partition::Partition;
5+
mod disk;
6+
use std::{fs, io, path::PathBuf};
117

8+
pub use disk::*;
129
pub mod nvme;
1310
pub mod partition;
1411
pub mod scsi;
@@ -25,151 +22,6 @@ pub enum BlockDevice {
2522
Unknown,
2623
}
2724

28-
/// Represents the type of disk device.
29-
#[derive(Debug)]
30-
pub enum Disk {
31-
/// SCSI disk device (e.g. sda, sdb)
32-
Scsi(scsi::Disk),
33-
/// NVMe disk device (e.g. nvme0n1)
34-
Nvme(nvme::Disk),
35-
}
36-
37-
/// A basic disk representation containing common attributes shared by all disk types.
38-
/// This serves as the base structure that specific disk implementations build upon.
39-
#[derive(Debug)]
40-
pub struct BasicDisk {
41-
/// Device name (e.g. sda, nvme0n1)
42-
pub name: String,
43-
/// Total number of sectors on the disk
44-
pub sectors: u64,
45-
/// Path to the device in sysfs
46-
pub node: PathBuf,
47-
/// Path to the device in /dev
48-
pub device: PathBuf,
49-
/// Optional disk model name
50-
pub model: Option<String>,
51-
/// Optional disk vendor name
52-
pub vendor: Option<String>,
53-
/// Partitions
54-
pub partitions: Vec<Partition>,
55-
}
56-
57-
impl fmt::Display for Disk {
58-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59-
let bytes = self.size();
60-
let gib = bytes as f64 / 1_073_741_824.0;
61-
62-
write!(f, "{} ({:.2} GiB)", self.name(), gib)?;
63-
64-
if let Some(vendor) = self.vendor() {
65-
write!(f, " - {}", vendor)?;
66-
}
67-
68-
if let Some(model) = self.model() {
69-
write!(f, " {}", model)?;
70-
}
71-
72-
Ok(())
73-
}
74-
}
75-
76-
impl Disk {
77-
/// Returns the name of the disk device.
78-
pub fn name(&self) -> &str {
79-
match self {
80-
Disk::Scsi(disk) => &disk.name,
81-
Disk::Nvme(disk) => &disk.name,
82-
}
83-
}
84-
85-
/// Returns the partitions on the disk.
86-
pub fn partitions(&self) -> &[Partition] {
87-
match self {
88-
Disk::Scsi(disk) => &disk.partitions,
89-
Disk::Nvme(disk) => &disk.partitions,
90-
}
91-
}
92-
93-
/// Returns the path to the disk device in dev.
94-
pub fn device_path(&self) -> &Path {
95-
match self {
96-
Disk::Scsi(disk) => &disk.device,
97-
Disk::Nvme(disk) => &disk.device,
98-
}
99-
}
100-
101-
/// Returns the total number of sectors on the disk.
102-
pub fn sectors(&self) -> u64 {
103-
match self {
104-
Disk::Scsi(disk) => disk.sectors,
105-
Disk::Nvme(disk) => disk.sectors,
106-
}
107-
}
108-
109-
/// Returns the size of the disk in bytes.
110-
pub fn size(&self) -> u64 {
111-
self.sectors() * 512
112-
}
113-
114-
/// Returns the model name of the disk.
115-
pub fn model(&self) -> Option<&str> {
116-
match self {
117-
Disk::Scsi(disk) => disk.model.as_deref(),
118-
Disk::Nvme(disk) => disk.model.as_deref(),
119-
}
120-
}
121-
122-
/// Returns the vendor name of the disk.
123-
pub fn vendor(&self) -> Option<&str> {
124-
match self {
125-
Disk::Scsi(disk) => disk.vendor.as_deref(),
126-
Disk::Nvme(disk) => disk.vendor.as_deref(),
127-
}
128-
}
129-
}
130-
131-
/// Trait for initializing different types of disk devices from sysfs.
132-
pub(crate) trait DiskInit: Sized {
133-
/// Creates a new disk instance by reading information from the specified sysfs path.
134-
///
135-
/// # Arguments
136-
///
137-
/// * `root` - The root sysfs directory path
138-
/// * `name` - The name of the disk device
139-
///
140-
/// # Returns
141-
///
142-
/// `Some(Self)` if the disk was successfully initialized, `None` otherwise
143-
fn from_sysfs_path(root: &Path, name: &str) -> Option<Self>;
144-
}
145-
146-
impl DiskInit for BasicDisk {
147-
fn from_sysfs_path(sysroot: &Path, name: &str) -> Option<Self> {
148-
let node = sysroot.join(name);
149-
150-
// Read the partitions of the disk if any
151-
let mut partitions: Vec<_> = fs::read_dir(&node)
152-
.ok()?
153-
.filter_map(Result::ok)
154-
.filter_map(|e| {
155-
let name = e.file_name().to_string_lossy().to_string();
156-
Partition::from_sysfs_path(sysroot, &name)
157-
})
158-
.collect();
159-
partitions.sort_by_key(|p| p.number);
160-
161-
Some(Self {
162-
name: name.to_owned(),
163-
sectors: sysfs::sysfs_read(sysroot, &node, "size").unwrap_or(0),
164-
device: PathBuf::from(DEVFS_DIR).join(name),
165-
model: sysfs::sysfs_read(sysroot, &node, "device/model"),
166-
vendor: sysfs::sysfs_read(sysroot, &node, "device/vendor"),
167-
partitions,
168-
node,
169-
})
170-
}
171-
}
172-
17325
impl BlockDevice {
17426
/// Discovers all block devices present in the system.
17527
///

0 commit comments

Comments
 (0)