that's fine for now
							parent
							
								
									4363fa1f19
								
							
						
					
					
						commit
						94d4845409
					
				|  | @ -2,11 +2,44 @@ | |||
| # It is not intended for manual editing. | ||||
| version = 4 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "addr2line" | ||||
| version = "0.24.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" | ||||
| dependencies = [ | ||||
|  "gimli", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "adler2" | ||||
| version = "2.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anyhow" | ||||
| version = "1.0.97" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" | ||||
| dependencies = [ | ||||
|  "backtrace", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "backtrace" | ||||
| version = "0.3.74" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" | ||||
| dependencies = [ | ||||
|  "addr2line", | ||||
|  "cfg-if", | ||||
|  "libc", | ||||
|  "miniz_oxide", | ||||
|  "object", | ||||
|  "rustc-demangle", | ||||
|  "windows-targets", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
|  | @ -54,6 +87,12 @@ dependencies = [ | |||
|  "wasi", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "gimli" | ||||
| version = "0.31.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.171" | ||||
|  | @ -75,6 +114,30 @@ dependencies = [ | |||
|  "tempfile", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "memchr" | ||||
| version = "2.7.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "miniz_oxide" | ||||
| version = "0.8.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" | ||||
| dependencies = [ | ||||
|  "adler2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "object" | ||||
| version = "0.36.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" | ||||
| dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "once_cell" | ||||
| version = "1.21.1" | ||||
|  | @ -87,6 +150,12 @@ version = "5.2.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "rustc-demangle" | ||||
| version = "0.1.24" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "rustix" | ||||
| version = "1.0.3" | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ name = "lk" | |||
| path = "src/main.rs" | ||||
| 
 | ||||
| [dependencies] | ||||
| anyhow = { version = "1.0.97" } | ||||
| anyhow = { version = "1.0.97", features = ["backtrace"] } | ||||
| camino = "1.1.9" | ||||
| 
 | ||||
| [dev-dependencies] | ||||
|  |  | |||
							
								
								
									
										101
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										101
									
								
								src/main.rs
								
								
								
								
							|  | @ -1,4 +1,5 @@ | |||
| use anyhow::{Context as _, Result}; | ||||
| use anyhow::{Context as _, Result, anyhow}; | ||||
| use camino::{Utf8Path, Utf8PathBuf}; | ||||
| use std::{ | ||||
|     borrow::Cow, | ||||
|     fs, | ||||
|  | @ -14,7 +15,7 @@ fn main() -> Result<()> { | |||
| struct App { | ||||
|     is_terminal: bool, | ||||
|     lk: Option<Lk>, | ||||
|     paths: Vec<Cow<'static, str>>, | ||||
|     paths: Vec<Cow<'static, Utf8Path>>, | ||||
|     path_index: usize, | ||||
| } | ||||
| 
 | ||||
|  | @ -27,15 +28,20 @@ impl App { | |||
|         let _exe_name = args.next().context("How do we not have an exe name?")?; | ||||
|         let mut paths = vec![]; | ||||
|         for arg in args { | ||||
|             paths.push(arg.into()); | ||||
|             paths.push(Cow::Owned(Utf8PathBuf::from(arg))); | ||||
|         } | ||||
|         let paths = if paths.is_empty() { | ||||
|             vec![".".into()] | ||||
|             vec![Cow::Borrowed(Utf8Path::new("."))] | ||||
|         } else { | ||||
|             paths | ||||
|         }; | ||||
| 
 | ||||
|         Ok(App { is_terminal, lk: None, paths, path_index: 0 }) | ||||
|         Ok(App { | ||||
|             is_terminal, | ||||
|             lk: None, | ||||
|             paths, | ||||
|             path_index: 0, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fn poll(&mut self) -> Result<Option<Rc<[u8]>>> { | ||||
|  | @ -45,11 +51,13 @@ impl App { | |||
|             } | ||||
|             let path = &self.paths[self.path_index]; | ||||
|             match self.lk.as_mut() { | ||||
|                 Some(lk) => if let Some(buf) = lk.poll()? { | ||||
|                     return Ok(Some(buf)); | ||||
|                 } else { | ||||
|                     self.lk = None; | ||||
|                     self.path_index += 1; | ||||
|                 Some(lk) => { | ||||
|                     if let Some(buf) = lk.poll()? { | ||||
|                         return Ok(Some(buf)); | ||||
|                     } else { | ||||
|                         self.lk = None; | ||||
|                         self.path_index += 1; | ||||
|                     } | ||||
|                 } | ||||
|                 None => { | ||||
|                     self.lk = Some(Lk::try_new(path)?); | ||||
|  | @ -72,7 +80,7 @@ enum Lk { | |||
| } | ||||
| 
 | ||||
| impl Lk { | ||||
|     fn try_new(path: &str) -> Result<Self> { | ||||
|     fn try_new(path: &Utf8Path) -> Result<Self> { | ||||
|         let metadata = fs::metadata(path)?; | ||||
|         if metadata.is_dir() { | ||||
|             Self::try_new_ls(path) | ||||
|  | @ -81,14 +89,15 @@ impl Lk { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn try_new_cat(path: &str) -> Result<Self> { | ||||
|     fn try_new_cat(path: &Utf8Path) -> Result<Self> { | ||||
|         Ok(Self::Cat(Cat::try_new(path)?)) | ||||
|     } | ||||
| 
 | ||||
|     fn try_new_ls(path: &str) -> Result<Self> { | ||||
|     fn try_new_ls(path: &Utf8Path) -> Result<Self> { | ||||
|         Ok(Self::Ls(Ls::try_new(path)?)) | ||||
|     } | ||||
| 
 | ||||
|     // I don't understand why, but using `Rc` here instead of `Cow` satisfies the borrow checker.
 | ||||
|     fn poll(&mut self) -> Result<Option<Rc<[u8]>>> { | ||||
|         match self { | ||||
|             Lk::Cat(x) => x.poll(), | ||||
|  | @ -103,7 +112,7 @@ struct Cat { | |||
| } | ||||
| 
 | ||||
| impl Cat { | ||||
|     fn try_new(path: &str) -> Result<Self> { | ||||
|     fn try_new(path: &Utf8Path) -> Result<Self> { | ||||
|         let f = fs::File::open(path)?; | ||||
|         let buf = vec![0u8; 4_096]; | ||||
|         Ok(Self { f, buf }) | ||||
|  | @ -120,34 +129,80 @@ impl Cat { | |||
| } | ||||
| 
 | ||||
| struct Ls { | ||||
|     d: fs::ReadDir, | ||||
|     names: Vec<String>, | ||||
|     name_index: usize, | ||||
| } | ||||
| 
 | ||||
| impl Ls { | ||||
|     fn try_new(path: &str) -> Result<Self> { | ||||
|     fn try_new(path: &Utf8Path) -> Result<Self> { | ||||
|         let d = fs::read_dir(path)?; | ||||
|         Ok(Self { d }) | ||||
|         let mut names = vec![]; | ||||
|         for entry in d { | ||||
|             let entry = entry?; | ||||
|             names.push( | ||||
|                 entry | ||||
|                     .file_name() | ||||
|                     .into_string() | ||||
|                     .map_err(|_| anyhow!("name is not UTF-8"))?, | ||||
|             ); | ||||
|         } | ||||
|         names.sort(); | ||||
|         Ok(Self { | ||||
|             names, | ||||
|             name_index: 0, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fn poll(&mut self) -> Result<Option<Rc<[u8]>>> { | ||||
|         let Some(entry) = self.d.next() else { | ||||
|         if self.name_index >= self.names.len() { | ||||
|             return Ok(None); | ||||
|         }; | ||||
|         let entry = entry?; | ||||
|         let path = entry.file_name(); | ||||
|         let path = path.to_str().context("file name is not valid Unicode")?; | ||||
|         Ok(Some(format!("{path}\n").into_bytes().into())) | ||||
|         } | ||||
|         let name = &self.names[self.name_index]; | ||||
|         self.name_index += 1; | ||||
|         Ok(Some(format!("{name}\n").into_bytes().into())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use std::path::Path; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test() -> Result<()> { | ||||
|         let dir = tempfile::tempdir()?; | ||||
|         let dir = dir.as_ref(); | ||||
|         fs::write(dir.join("alfa.txt"), "alfa\n")?; | ||||
|         fs::write(dir.join("bravo.txt"), "bravo\n")?; | ||||
| 
 | ||||
|         for (input, expected) in [ | ||||
|             (vec![dir], b"alfa.txt\nbravo.txt\n".to_vec()), | ||||
|             ( | ||||
|                 vec![&dir.join("alfa.txt"), &dir.join("bravo.txt")], | ||||
|                 b"alfa\nbravo\n".to_vec(), | ||||
|             ), | ||||
|         ] { | ||||
|             assert_eq!(lk(input)?, expected); | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn lk(paths: Vec<&Path>) -> Result<Vec<u8>> { | ||||
|         let paths = paths | ||||
|             .iter() | ||||
|             .map(|p| Cow::Owned(Utf8PathBuf::from_path_buf(p.to_path_buf()).unwrap())) | ||||
|             .collect(); | ||||
|         let mut app = App { | ||||
|             is_terminal: false, | ||||
|             lk: None, | ||||
|             paths, | ||||
|             path_index: 0, | ||||
|         }; | ||||
|         let mut out = vec![]; | ||||
|         while let Some(buf) = app.poll()? { | ||||
|             out.write_all(&buf)?; | ||||
|         } | ||||
|         Ok(out) | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 _
						_