Skip to main content

fancy_tree/config/icons/
mod.rs

1//! Module for the icon config.
2use super::ConfigFile;
3use crate::icons;
4use crate::lua::interop;
5use crate::tree::{
6    Entry,
7    entry::{Attributes, attributes::FileAttributes},
8};
9use mlua::{FromLua, Lua};
10use std::path::Path;
11
12/// The configuration for icons.
13#[derive(Debug, Default)]
14pub struct Icons {
15    /// Function to get the icon for an entry.
16    get_icon: Option<mlua::Function>,
17}
18
19impl Icons {
20    /// The default icon to display for files.
21    const DEFAULT_FILE_ICON: &'static str = "\u{f0214}"; // 󰈔
22    /// The default icon to display when a file is an executable.
23    const DEFAULT_EXECUTABLE_ICON: &'static str = "\u{f070e}"; // 󰜎
24    /// The default icon to display for directories/folders.
25    const DEFAULT_DIRECTORY_ICON: &'static str = "\u{f024b}"; // 󰉋
26    /// The default icon to display for symlinks.
27    const DEFAULT_SYMLINK_ICON: &'static str = "\u{cf481}"; // 
28
29    /// The icon (padding) to use if there is no icon.
30    const EMPTY_ICON: &'static str = " ";
31
32    /// Get the icon for the entry. If the configuration returns `nil`, a string with
33    /// invisible characters will be returned.
34    ///
35    /// On a Lua error, this falls back to the default icon choice.
36    pub fn get_icon<P>(&self, entry: &Entry<P>) -> String
37    where
38        P: AsRef<Path>,
39    {
40        // TODO Use Cow
41        let default_icon =
42            icons::for_path(entry.path()).unwrap_or_else(|| Self::default_icon(entry));
43        self.get_icon
44            .as_ref()
45            .and_then(|f| {
46                let path = entry.path();
47                let attributes = interop::FileAttributes::from(entry);
48                // TODO Report the error when this function fails
49                f.call::<Option<String>>((path, attributes, default_icon))
50                    .ok()
51            })
52            .unwrap_or_else(|| Some(String::from(default_icon)))
53            .unwrap_or_else(|| String::from(Self::EMPTY_ICON))
54    }
55
56    /// Gets the default icon choice for an entry.
57    fn default_icon<P>(entry: &Entry<P>) -> &str
58    where
59        P: AsRef<Path>,
60    {
61        match entry.attributes() {
62            Attributes::Directory(_) => Self::DEFAULT_DIRECTORY_ICON,
63            Attributes::File(attributes) => Self::get_file_icon(attributes),
64            Attributes::Symlink(_) => Self::DEFAULT_SYMLINK_ICON,
65        }
66    }
67
68    /// Gets the default icon for a file entry.
69    fn get_file_icon(attributes: &FileAttributes) -> &'static str {
70        if attributes.is_executable() {
71            return Self::DEFAULT_EXECUTABLE_ICON;
72        }
73        attributes
74            .language()
75            .and_then(|language| language.nerd_font_glyph())
76            .unwrap_or(Self::DEFAULT_FILE_ICON)
77    }
78}
79
80impl ConfigFile for Icons {
81    const FILENAME: &'static str = "icons.lua";
82    const DEFAULT_MODULE: &'static str = include_str!("./icons.lua");
83}
84
85impl FromLua for Icons {
86    fn from_lua(value: mlua::Value, lua: &Lua) -> mlua::Result<Self> {
87        Option::<mlua::Function>::from_lua(value, lua).map(|get_icon| Self { get_icon })
88    }
89}