Syntax aware cat
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rework errors again

+94 -102
-7
Cargo.lock
··· 69 69 ] 70 70 71 71 [[package]] 72 - name = "anyhow" 73 - version = "1.0.75" 74 - source = "registry+https://github.com/rust-lang/crates.io-index" 75 - checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 76 - 77 - [[package]] 78 72 name = "atty" 79 73 version = "0.2.14" 80 74 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 595 589 version = "3.4.0" 596 590 dependencies = [ 597 591 "ansi_term", 598 - "anyhow", 599 592 "cc", 600 593 "clap", 601 594 "console",
-1
syncat/Cargo.toml
··· 34 34 tempdir = "0.3.7" 35 35 clap = { version = "4.4.6", features = ["derive"] } 36 36 tar = "0.4.40" 37 - anyhow = "1.0.75" 38 37 39 38 [build-dependencies] 40 39 tar = "0.4.40"
-40
syncat/src/config/error.rs
··· 1 - use std::fmt::{self, Display}; 2 - 3 - #[derive(Debug)] 4 - pub struct ConfigError { 5 - pub(super) message: &'static str, 6 - pub(super) source: Option<Box<dyn std::error::Error + Sync + Send>>, 7 - } 8 - 9 - impl Display for ConfigError { 10 - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 11 - match &self.source { 12 - Some(source) => source.fmt(f), 13 - None => self.message.fmt(f), 14 - } 15 - } 16 - } 17 - 18 - impl std::error::Error for ConfigError { 19 - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 20 - self.source.as_ref().map(|s| &**s as _) 21 - } 22 - } 23 - 24 - impl From<std::io::Error> for ConfigError { 25 - fn from(value: std::io::Error) -> Self { 26 - Self { 27 - message: "IO Error", 28 - source: Some(Box::new(value)), 29 - } 30 - } 31 - } 32 - 33 - impl From<syncat_stylesheet::Error> for ConfigError { 34 - fn from(value: syncat_stylesheet::Error) -> Self { 35 - Self { 36 - message: "Stylesheet Error", 37 - source: Some(Box::new(value)), 38 - } 39 - } 40 - }
+32 -23
syncat/src/config/mod.rs syncat/src/config.rs
··· 3 3 use std::{fs, io}; 4 4 use syncat_stylesheet::Stylesheet; 5 5 6 - mod error; 7 - 8 - pub use error::ConfigError; 9 - 10 6 const DEFAULT_CONFIG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/config.tar")); 11 7 12 8 fn normalize(path: &Path) -> PathBuf { ··· 25 21 components.into_iter().collect() 26 22 } 27 23 28 - fn read_config<P: AsRef<Path>>(file: P) -> Result<String, ConfigError> { 24 + fn read_config<P: AsRef<Path>>(file: P) -> crate::Result<String> { 29 25 let normalized = normalize(file.as_ref()); 30 26 let mut config_reader = DEFAULT_CONFIG; 31 - for entry in tar::Archive::new(&mut config_reader).entries().unwrap() { 32 - let entry = entry?; 27 + for entry in tar::Archive::new(&mut config_reader) 28 + .entries() 29 + .unwrap() 30 + .map(Result::unwrap) 31 + { 33 32 if entry.path().unwrap().as_ref() == normalized { 34 - return Ok(io::read_to_string(entry)?); 33 + return Ok(io::read_to_string(entry).expect("default config should be valid")); 35 34 } 36 35 } 37 - Err(ConfigError { 38 - message: "file not found in default configuration", 39 - source: None, 40 - }) 36 + Err(crate::Error::new("file not found in default configuration")) 41 37 } 42 38 43 39 fn config_exists<P: AsRef<Path>>(file: P) -> bool { ··· 48 44 .any(|res| res.unwrap().path().unwrap().as_ref() == file.as_ref()) 49 45 } 50 46 51 - pub fn read_to_string<P: AsRef<Path>>(file: P) -> Result<String, ConfigError> { 52 - if config().join(&file).exists() { 53 - Ok(fs::read_to_string(config().join(file))?) 47 + pub(crate) fn read_to_string<P: AsRef<Path>>(file: P) -> crate::Result<String> { 48 + let path = config().join(&file); 49 + if path.exists() { 50 + Ok(fs::read_to_string(&path).map_err(|er| { 51 + crate::Error::new("could not read config file") 52 + .with_source(er) 53 + .with_path(path) 54 + })?) 54 55 } else { 55 56 read_config(file) 56 57 } 57 58 } 58 59 59 - pub fn load_stylesheet<P: AsRef<Path>>(file: P) -> Result<Option<Stylesheet>, ConfigError> { 60 - if active_color().join(&file).exists() { 61 - Ok(Some(Stylesheet::from_file(active_color().join(&file))?)) 60 + pub(crate) fn load_stylesheet<P: AsRef<Path>>(file: P) -> crate::Result<Option<Stylesheet>> { 61 + let path = active_color().join(&file); 62 + if path.exists() { 63 + Ok(Some(Stylesheet::from_file(&path).map_err(|er| { 64 + crate::Error::new("could not read stylesheet") 65 + .with_source(er) 66 + .with_path(path) 67 + })?)) 62 68 } else if !config_exists(Path::new("style/active").join(&file)) { 63 69 Ok(None) 64 70 } else { 65 - Ok(Some(Stylesheet::from_file_with_resolver( 66 - Path::new("style/active").join(file), 67 - &ConstResolver, 68 - )?)) 71 + Ok(Some( 72 + Stylesheet::from_file_with_resolver( 73 + Path::new("style/active").join(file), 74 + &ConstResolver, 75 + ) 76 + .unwrap(), 77 + )) 69 78 } 70 79 } 71 80 ··· 91 100 struct ConstResolver; 92 101 93 102 impl syncat_stylesheet::resolver::Resolver for ConstResolver { 94 - type Error = ConfigError; 103 + type Error = crate::Error; 95 104 96 105 fn read_to_string<P: AsRef<Path>>(&self, path: P) -> Result<String, Self::Error> { 97 106 read_config(path)
+31 -15
syncat/src/language.rs
··· 1 - use crate::config::{self, ConfigError}; 1 + use crate::config; 2 2 use crate::dirs::libraries; 3 3 use libloading::{Library, Symbol}; 4 4 use std::borrow::Borrow; ··· 12 12 /// The list of languages, found in `languages.toml` in the config directory. 13 13 #[derive(serde::Deserialize, Default)] 14 14 #[serde(transparent)] 15 - pub struct LangMap(BTreeMap<String, Lang>); 15 + pub(crate) struct LangMap(BTreeMap<String, Lang>); 16 16 17 17 impl LangMap { 18 - pub fn open() -> anyhow::Result<Self> { 18 + pub(crate) fn open() -> crate::Result<Self> { 19 19 match config::read_to_string("languages.toml") { 20 - Ok(string) => Ok(toml::from_str(&string)?), 20 + Ok(string) => Ok(toml::from_str(&string) 21 + .map_err(|er| crate::Error::new("failed to parse language map").with_source(er))?), 21 22 Err(..) => Ok(LangMap::default()), 22 23 } 23 24 } 24 25 25 - pub fn get(&self, name: &str) -> Option<&Lang> { 26 + pub(crate) fn get(&self, name: &str) -> Option<&Lang> { 26 27 self.0 27 28 .iter() 28 29 .find(|(key, lang)| *key == name || lang.extensions.iter().any(|i| i == name)) 29 30 .map(|(.., lang)| lang) 30 31 } 31 32 32 - pub fn get_strict<Q>(&self, name: &Q) -> Option<&Lang> 33 + pub(crate) fn get_strict<Q>(&self, name: &Q) -> Option<&Lang> 33 34 where 34 35 Q: Eq + Ord, 35 36 String: Borrow<Q>, ··· 71 72 } 72 73 73 74 impl Lang { 74 - fn load(&self) -> anyhow::Result<bool> { 75 + fn load(&self) -> crate::Result<bool> { 75 76 if self.lib.borrow().is_some() { 76 77 return Ok(true); 77 78 } ··· 82 83 if !lib_dir.exists() { 83 84 return Ok(false); 84 85 } 85 - let lib_name = fs::read_dir(&lib_dir)? 86 + let lib_name = fs::read_dir(&lib_dir) 87 + .map_err(|er| { 88 + crate::Error::new("language directory not found") 89 + .with_source(er) 90 + .with_path(&lib_dir) 91 + })? 86 92 .filter_map(|entry| entry.ok()) 87 93 .map(|entry| entry.path()) 88 94 .find(|path| { ··· 92 98 .filter(|path| path.contains("syncat")) 93 99 .is_some() 94 100 }) 95 - .ok_or_else(|| anyhow::anyhow!("Language is not installed correctly."))?; 96 - let library = Library::new(lib_dir.join(lib_name))?; 101 + .ok_or_else(|| crate::Error::new("language is not installed correctly."))?; 102 + let library = Library::new(lib_dir.join(lib_name)) 103 + .map_err(|er| crate::Error::new("language could not be loaded").with_source(er))?; 97 104 *self.lib.borrow_mut() = Some(library); 98 105 Ok(true) 99 106 } 100 107 101 - pub fn parser(&self) -> anyhow::Result<Option<Parser>> { 108 + pub(crate) fn parser(&self) -> crate::Result<Option<Parser>> { 102 109 if !self.load()? { 103 110 return Ok(None); 104 111 } 105 112 let language = unsafe { 106 113 let borrow = self.lib.borrow(); 107 114 let lib = borrow.as_ref().unwrap(); 108 - let get_language: Symbol<unsafe extern "C" fn() -> Language> = 109 - lib.get(format!("tree_sitter_{}", self.name).as_bytes())?; 115 + let get_language: Symbol<unsafe extern "C" fn() -> Language> = lib 116 + .get(format!("tree_sitter_{}", self.name).as_bytes()) 117 + .map_err(|er| { 118 + crate::Error::new(format!( 119 + "tree_sitter_{} not found in language library", 120 + self.name 121 + )) 122 + .with_source(er) 123 + })?; 110 124 get_language() 111 125 }; 112 126 113 127 let mut parser = Parser::new(); 114 - parser.set_language(language)?; 128 + parser 129 + .set_language(language) 130 + .map_err(|er| crate::Error::new("tree sitter").with_source(er))?; 115 131 Ok(Some(parser)) 116 132 } 117 133 118 - pub fn style(&self) -> Result<Stylesheet, ConfigError> { 134 + pub(crate) fn style(&self) -> crate::Result<Stylesheet> { 119 135 config::load_stylesheet(Path::new(&self.name).with_extension("syncat")) 120 136 .map(|opt| opt.unwrap_or_default()) 121 137 }
+16 -11
syncat/src/main.rs
··· 1 + use clap::{ArgAction, Parser}; 1 2 use std::fs; 2 3 use std::io::{self, Read}; 3 4 use std::path::{Path, PathBuf}; 4 - 5 - use clap::{ArgAction, Parser}; 6 5 7 6 mod colorize; 8 7 mod config; ··· 15 14 mod package_manager; 16 15 17 16 use colorize::Colorizer; 18 - use error::Error; 17 + use error::{Error, Result}; 19 18 use language::LangMap; 20 19 use line::Line; 21 20 use meta_stylesheet::MetaStylesheet; ··· 103 102 } 104 103 105 104 impl Syncat { 106 - fn new(opts: Opts) -> anyhow::Result<Self> { 105 + fn new(opts: Opts) -> error::Result<Self> { 107 106 let lang_map = LangMap::open()?; 108 107 let meta_style = MetaStylesheet::from_file()?; 109 108 Ok(Self { ··· 113 112 }) 114 113 } 115 114 116 - fn colorize(&self, language: Option<&str>, source: String) -> anyhow::Result<String> { 115 + fn colorize(&self, language: Option<&str>, source: String) -> crate::Result<String> { 117 116 let language = self 118 117 .opts 119 118 .language ··· 147 146 language: Option<&str>, 148 147 source: String, 149 148 path: Option<&Path>, 150 - ) -> anyhow::Result<Vec<Line>> { 149 + ) -> crate::Result<Vec<Line>> { 151 150 let source = self.colorize(language, source)?; 152 151 153 152 if self.opts.dev { ··· 175 174 fn print<'a>( 176 175 &self, 177 176 sources: impl IntoIterator<Item = error::Result<Source<'a>>> + ExactSizeIterator, 178 - ) -> anyhow::Result<()> { 177 + ) -> crate::Result<()> { 179 178 let count = sources.len(); 180 179 let mut line_numbers = filter::line_numbers(&self.opts); 181 180 for (index, source) in sources.into_iter().enumerate() { ··· 223 222 } 224 223 } 225 224 226 - fn try_main() -> anyhow::Result<()> { 225 + fn try_main() -> error::Result<()> { 227 226 let opts = Opts::parse(); 228 227 match &opts.command { 229 228 Some(subcommand) => package_manager::main(subcommand), ··· 232 231 // These lines cannot be syntax highlighted, as we do not know what the language is. 233 232 loop { 234 233 let mut line = String::new(); 235 - if io::stdin().read_line(&mut line)? == 0 { 234 + if io::stdin() 235 + .read_line(&mut line) 236 + .map_err(|er| crate::Error::new("could not read stdin").with_source(er))? 237 + == 0 238 + { 236 239 return Ok(()); 237 240 } 238 241 print!("{}", line); ··· 243 246 // at once using the specified language. 244 247 let mut stdin = io::stdin(); 245 248 let mut source = String::new(); 246 - stdin.read_to_string(&mut source)?; 249 + stdin 250 + .read_to_string(&mut source) 251 + .map_err(|er| crate::Error::new("could not read stdin").with_source(er))?; 247 252 let syncat = Syncat::new(opts)?; 248 253 syncat.print(std::iter::once(Ok(Source { 249 254 language: None, ··· 279 284 280 285 fn main() { 281 286 if let Err(error) = try_main() { 282 - eprintln!("syncat: {}", error); 287 + eprintln!("{}", error); 283 288 std::process::exit(1); 284 289 } 285 290 }
+10 -2
syncat/src/meta_stylesheet.rs
··· 169 169 } 170 170 171 171 impl MetaStylesheet { 172 - pub fn from_file() -> anyhow::Result<MetaStylesheet> { 172 + pub(crate) fn from_file() -> crate::Result<MetaStylesheet> { 173 173 let mut meta_stylesheet = MetaStylesheet::default(); 174 - if let Some(stylesheet) = config::load_stylesheet(".syncat")? { 174 + if let Some(stylesheet) = config::load_stylesheet(".syncat") 175 + .map_err(|er| crate::Error::new("failed to load meta stylesheet").with_source(er))? 176 + { 175 177 if let Some(style) = stylesheet.style(&"line_ending".into()) { 176 178 meta_stylesheet.line_ending = style.try_into()?; 177 179 } ··· 232 234 &self.title.style 233 235 } 234 236 } 237 + 238 + impl From<FromValueError> for crate::Error { 239 + fn from(value: FromValueError) -> Self { 240 + Self::new("failed to parse meta stylesheet value").with_source(value) 241 + } 242 + }
+5 -3
syncat/src/package_manager.rs
··· 100 100 Ok(()) 101 101 } 102 102 103 - pub(crate) fn main(opts: &Subcommand) -> anyhow::Result<()> { 103 + pub(crate) fn main(opts: &Subcommand) -> crate::Result<()> { 104 104 let lang_map = LangMap::open()?; 105 105 if !libraries().exists() { 106 - fs::create_dir_all(libraries())?; 106 + // Don't really care about this error, it's a sketchy side effect anyway. 107 + fs::create_dir_all(libraries()).ok(); 107 108 } 108 109 109 110 match opts { ··· 132 133 Subcommand::Remove { language } => { 133 134 if let Some(lang) = lang_map.get_strict(language) { 134 135 let directory = libraries().join(&lang.library); 135 - fs::remove_dir_all(directory)?; 136 + fs::remove_dir_all(directory) 137 + .map_err(|er| crate::Error::new("failed to remove language").with_source(er))?; 136 138 println!("Language `{}` has been removed", language); 137 139 } else { 138 140 println!("No language `{}` is installed", language);