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 COLLAPSED_DIRECTORY_TYPE: &'static str = "collapsed_folder";
 21const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_folder";
 22const COLLAPSED_CHEVRON_TYPE: &'static str = "collapsed_chevron";
 23const EXPANDED_CHEVRON_TYPE: &'static str = "expanded_chevron";
 24pub const FILE_TYPES_ASSET: &'static str = "icons/file_icons/file_types.json";
 25
 26pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
 27    cx.set_global(FileAssociations::new(assets))
 28}
 29
 30impl FileAssociations {
 31    pub fn new(assets: impl AssetSource) -> Self {
 32        assets
 33            .load("icons/file_icons/file_types.json")
 34            .and_then(|file| {
 35                serde_json::from_str::<FileAssociations>(str::from_utf8(&file).unwrap())
 36                    .map_err(Into::into)
 37            })
 38            .unwrap_or_else(|_| FileAssociations {
 39                suffixes: HashMap::default(),
 40                types: HashMap::default(),
 41            })
 42    }
 43
 44    pub fn get_icon(path: &Path, cx: &AppContext) -> Arc<str> {
 45        iife!({
 46            let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
 47
 48            // FIXME: Associate a type with the languages and have the file's langauge
 49            //        override these associations
 50            iife!({
 51                let suffix = path
 52                    .file_name()
 53                    .and_then(|os_str| os_str.to_str())
 54                    .and_then(|file_name| {
 55                        file_name
 56                            .find('.')
 57                            .and_then(|dot_index| file_name.get(dot_index + 1..))
 58                    })?;
 59
 60                this.suffixes
 61                    .get(suffix)
 62                    .and_then(|type_str| this.types.get(type_str))
 63                    .map(|type_config| type_config.icon.clone())
 64            })
 65            .or_else(|| this.types.get("default").map(|config| config.icon.clone()))
 66        })
 67        .unwrap_or_else(|| Arc::from("".to_string()))
 68    }
 69
 70    pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Arc<str> {
 71        iife!({
 72            let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
 73
 74            let key = if expanded {
 75                EXPANDED_DIRECTORY_TYPE
 76            } else {
 77                COLLAPSED_DIRECTORY_TYPE
 78            };
 79
 80            this.types
 81                .get(key)
 82                .map(|type_config| type_config.icon.clone())
 83        })
 84        .unwrap_or_else(|| Arc::from("".to_string()))
 85    }
 86
 87    pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Arc<str> {
 88        iife!({
 89            let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
 90
 91            let key = if expanded {
 92                EXPANDED_CHEVRON_TYPE
 93            } else {
 94                COLLAPSED_CHEVRON_TYPE
 95            };
 96
 97            this.types
 98                .get(key)
 99                .map(|type_config| type_config.icon.clone())
100        })
101        .unwrap_or_else(|| Arc::from("".to_string()))
102    }
103}