on 90e7d2f help sign in
bat/src/syntax_mapping.rs
111 lines 3.0 kB history raw
1use std::path::Path;
3use crate::errors::Result;
5use globset::{Candidate, GlobBuilder, GlobMatcher};
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum MappingTarget<'a> {
9 MapTo(&'a str),
10 MapToUnknown,
13#[derive(Debug, Clone, Default)]
14pub struct SyntaxMapping<'a> {
15 mappings: Vec<(GlobMatcher, MappingTarget<'a>)>,
18impl<'a> SyntaxMapping<'a> {
19 pub fn empty() -> SyntaxMapping<'a> {
20 Default::default()
21 }
23 pub fn builtin() -> SyntaxMapping<'a> {
24 let mut mapping = Self::empty();
25 mapping.insert("*.h", MappingTarget::MapTo("C++")).unwrap();
26 mapping
27 .insert("build", MappingTarget::MapToUnknown)
28 .unwrap();
29 mapping
30 .insert("**/.ssh/config", MappingTarget::MapTo("SSH Config"))
31 .unwrap();
32 mapping
33 .insert(
34 "/etc/profile",
35 MappingTarget::MapTo("Bourne Again Shell (bash)"),
36 )
37 .unwrap();
39 mapping
40 }
42 pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {
43 let glob = GlobBuilder::new(from)
44 .case_insensitive(false)
45 .literal_separator(true)
46 .build()?;
47 self.mappings.push((glob.compile_matcher(), to));
48 Ok(())
49 }
51 pub(crate) fn get_syntax_for(&self, path: impl AsRef<Path>) -> Option<MappingTarget<'a>> {
52 let candidate = Candidate::new(path.as_ref());
53 let canddidate_filename = path.as_ref().file_name().map(Candidate::new);
54 for (ref glob, ref syntax) in self.mappings.iter().rev() {
55 if glob.is_match_candidate(&candidate)
56 || canddidate_filename
57 .as_ref()
58 .map_or(false, |filename| glob.is_match_candidate(filename))
59 {
60 return Some(*syntax);
61 }
62 }
63 None
64 }
67#[test]
68fn basic() {
69 let mut map = SyntaxMapping::empty();
70 map.insert("/path/to/Cargo.lock", MappingTarget::MapTo("TOML"))
71 .ok();
72 map.insert("/path/to/.ignore", MappingTarget::MapTo("Git Ignore"))
73 .ok();
75 assert_eq!(
76 map.get_syntax_for("/path/to/Cargo.lock"),
77 Some(MappingTarget::MapTo("TOML"))
78 );
79 assert_eq!(map.get_syntax_for("/path/to/other.lock"), None);
81 assert_eq!(
82 map.get_syntax_for("/path/to/.ignore"),
83 Some(MappingTarget::MapTo("Git Ignore"))
84 );
87#[test]
88fn user_can_override_builtin_mappings() {
89 let mut map = SyntaxMapping::builtin();
91 assert_eq!(
92 map.get_syntax_for("/etc/profile"),
93 Some(MappingTarget::MapTo("Bourne Again Shell (bash)"))
94 );
95 map.insert("/etc/profile", MappingTarget::MapTo("My Syntax"))
96 .ok();
97 assert_eq!(
98 map.get_syntax_for("/etc/profile"),
99 Some(MappingTarget::MapTo("My Syntax"))
100 );
103#[test]
104fn builtin_mappings() {
105 let map = SyntaxMapping::builtin();
107 assert_eq!(
108 map.get_syntax_for("/path/to/build"),
109 Some(MappingTarget::MapToUnknown)
110 );