Skip to main content

use_iso/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! ISO image labels and volume metadata for `RustUse`.
5
6use core::fmt;
7
8/// Common ISO image extension.
9pub const ISO_EXTENSION: &str = "iso";
10/// Common disk image extension used for ISO-like artifacts.
11pub const IMAGE_EXTENSION: &str = "img";
12/// Common ISO-related extensions.
13pub const ISO_EXTENSIONS: &[&str] = &["iso", "img"];
14
15/// ISO image format labels.
16#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
17pub enum IsoFormat {
18    /// ISO 9660 filesystem image.
19    Iso9660,
20    /// UDF filesystem image.
21    Udf,
22    /// Hybrid image label.
23    Hybrid,
24    /// Unknown or intentionally unspecified ISO image format.
25    #[default]
26    Unknown,
27}
28
29impl IsoFormat {
30    /// Returns a stable lowercase label.
31    #[must_use]
32    pub const fn as_str(self) -> &'static str {
33        match self {
34            Self::Iso9660 => "iso9660",
35            Self::Udf => "udf",
36            Self::Hybrid => "hybrid",
37            Self::Unknown => "unknown",
38        }
39    }
40}
41
42impl fmt::Display for IsoFormat {
43    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44        formatter.write_str(self.as_str())
45    }
46}
47
48/// ISO volume kind labels.
49#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
50pub enum IsoVolumeKind {
51    /// Bootable image label.
52    Bootable,
53    /// Data image label.
54    Data,
55    /// Audio image label.
56    Audio,
57    /// Hybrid volume label.
58    Hybrid,
59    /// Unknown or intentionally unspecified volume kind.
60    #[default]
61    Unknown,
62}
63
64impl IsoVolumeKind {
65    /// Returns a stable lowercase label.
66    #[must_use]
67    pub const fn as_str(self) -> &'static str {
68        match self {
69            Self::Bootable => "bootable",
70            Self::Data => "data",
71            Self::Audio => "audio",
72            Self::Hybrid => "hybrid",
73            Self::Unknown => "unknown",
74        }
75    }
76}
77
78impl fmt::Display for IsoVolumeKind {
79    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
80        formatter.write_str(self.as_str())
81    }
82}
83
84/// Returns whether `extension` is a known ISO image extension label.
85#[must_use]
86pub fn is_iso_extension(extension: &str) -> bool {
87    matches!(normalize_extension(extension).as_str(), "iso" | "img")
88}
89
90/// Returns whether `name` has a known ISO image filename encoding.
91#[must_use]
92pub fn is_iso_filename(name: &str) -> bool {
93    let parts = filename_parts(name);
94
95    matches!(parts.as_slice(), [.., last] if matches!(last.as_str(), "iso" | "img"))
96}
97
98fn normalize_extension(extension: &str) -> String {
99    extension
100        .trim()
101        .trim_start_matches('.')
102        .to_ascii_lowercase()
103}
104
105fn filename_parts(name: &str) -> Vec<String> {
106    name.trim()
107        .to_ascii_lowercase()
108        .rsplit(['/', '\\'])
109        .next()
110        .unwrap_or_default()
111        .trim_start_matches('.')
112        .split('.')
113        .filter(|part| !part.is_empty())
114        .map(str::to_owned)
115        .collect()
116}
117
118#[cfg(test)]
119mod tests {
120    use super::{ISO_EXTENSIONS, IsoFormat, IsoVolumeKind, is_iso_extension, is_iso_filename};
121
122    #[test]
123    fn detects_iso_extensions() {
124        assert!(is_iso_extension(".iso"));
125        assert!(is_iso_extension("img"));
126        assert_eq!(ISO_EXTENSIONS[0], "iso");
127    }
128
129    #[test]
130    fn detects_iso_filenames() {
131        assert!(is_iso_filename("installer.iso"));
132        assert!(is_iso_filename("disk.IMG"));
133        assert!(!is_iso_filename("bundle.zip"));
134    }
135
136    #[test]
137    fn exposes_default_and_unknown_labels() {
138        assert_eq!(IsoFormat::default(), IsoFormat::Unknown);
139        assert_eq!(IsoFormat::Udf.as_str(), "udf");
140        assert_eq!(IsoVolumeKind::default(), IsoVolumeKind::Unknown);
141        assert_eq!(IsoVolumeKind::Audio.as_str(), "audio");
142    }
143}