file_associations.rs

 1use std::{path::Path, str, sync::Arc};
 2
 3use collections::HashMap;
 4
 5use gpui::{AppContext, AssetSource};
 6use serde_derive::Deserialize;
 7use util::iife;
 8
 9#[derive(Deserialize, Debug)]
10struct TypeConfig {
11    icon: Arc<str>,
12}
13
14#[derive(Deserialize, Debug)]
15pub struct FileAssociations {
16    suffixes: HashMap<String, String>,
17    types: HashMap<String, TypeConfig>,
18}
19
20const DIRECTORY_TYPE: &'static str = "directory";
21const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_directory";
22pub const FILE_TYPES_ASSET: &'static str = "icons/file_icons/file_types.json";
23
24pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
25    cx.set_global(FileAssociations::new(assets))
26}
27
28impl FileAssociations {
29    pub fn new(assets: impl AssetSource) -> Self {
30        assets
31            .load("icons/file_icons/file_types.json")
32            .and_then(|file| {
33                serde_json::from_str::<FileAssociations>(str::from_utf8(&file).unwrap())
34                    .map_err(Into::into)
35            })
36            .unwrap_or_else(|_| FileAssociations {
37                suffixes: HashMap::default(),
38                types: HashMap::default(),
39            })
40    }
41
42    pub fn get_icon(path: &Path, cx: &AppContext) -> Arc<str> {
43        iife!({
44            let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
45
46            // FIXME: Associate a type with the languages and have the file's langauge
47            //        override these associations
48            iife!({
49                let suffix = path
50                    .file_name()
51                    .and_then(|os_str| os_str.to_str())
52                    .and_then(|file_name| {
53                        file_name
54                            .find('.')
55                            .and_then(|dot_index| file_name.get(dot_index + 1..))
56                    })?;
57
58                this.suffixes
59                    .get(suffix)
60                    .and_then(|type_str| this.types.get(type_str))
61                    .map(|type_config| type_config.icon.clone())
62            })
63            .or_else(|| this.types.get("default").map(|config| config.icon.clone()))
64        })
65        .unwrap_or_else(|| Arc::from("".to_string()))
66    }
67
68    pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Arc<str> {
69        iife!({
70            let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
71
72            let key = if expanded {
73                EXPANDED_DIRECTORY_TYPE
74            } else {
75                DIRECTORY_TYPE
76            };
77
78            this.types
79                .get(key)
80                .map(|type_config| type_config.icon.clone())
81        })
82        .unwrap_or_else(|| Arc::from("".to_string()))
83    }
84}