shared_mime/runtime/
mimeinfo.rs

1//! XDG Shared MIME Info database representation.
2
3use std::{
4    fs::read_dir,
5    path::{Path, PathBuf},
6};
7
8use log::*;
9use serde::Serialize;
10
11use crate::record::MimeTypeRecord;
12
13use super::{parse_mime_package, xdg_mime_search_dirs, XDGError};
14
15/// Container for the fully-parsed Shared Mime Info across search directories.
16#[derive(Serialize, Debug, Clone)]
17pub struct SharedMimeInfo {
18    /// The XDG mime directories in processing order.
19    pub directories: Vec<SMIDir>,
20}
21
22/// A single directory in the shared mime database.
23#[derive(Serialize, Debug, Clone)]
24pub struct SMIDir {
25    pub path: PathBuf,
26    pub packages: Vec<SMIPackage>,
27}
28
29/// A single package within the shared mime database.
30#[derive(Serialize, Debug, Clone)]
31pub struct SMIPackage {
32    pub filename: String,
33    pub types: Vec<MimeTypeRecord>,
34}
35
36/// Load the full XDG mime info database from all available files.
37pub fn load_xdg_mime_info() -> Result<SharedMimeInfo, XDGError> {
38    let dirs = xdg_mime_search_dirs();
39    Ok(SharedMimeInfo {
40        directories: dirs
41            .into_iter()
42            .filter_map(|d| load_xdg_mime_dir(d).transpose())
43            .collect::<Result<Vec<_>, _>>()?,
44    })
45}
46
47fn load_xdg_mime_dir<P: AsRef<Path>>(path: P) -> Result<Option<SMIDir>, XDGError> {
48    let path = path.as_ref();
49    let mut buf = path.to_path_buf();
50    buf.push("packages");
51    debug!("looking for packages in {}", path.display());
52    if !buf.try_exists()? {
53        debug!("{} does not exist", buf.display());
54        return Ok(None);
55    }
56
57    let mut packages = Vec::new();
58    for entry in read_dir(&buf)? {
59        let entry = entry?;
60        let ep = entry.path();
61        let ext = ep.extension().map_or("".into(), |e| e.to_string_lossy());
62        debug!("extension {}", ext);
63        if entry.file_name().as_encoded_bytes()[0] != b'.' && ext == "xml" {
64            debug!("reading package file {}", ep.display());
65            packages.push(SMIPackage {
66                filename: ep
67                    .file_name()
68                    .ok_or(XDGError::Layout("package missing filename".into()))?
69                    .to_string_lossy()
70                    .to_string(),
71                types: parse_mime_package(&ep)?
72                    .types
73                    .into_iter()
74                    .map(MimeTypeRecord::from)
75                    .collect(),
76            })
77        } else {
78            debug!("ignoring file {}", ep.display());
79        }
80    }
81
82    Ok(Some(SMIDir {
83        path: path.to_path_buf(),
84        packages,
85    }))
86}