From d1be9c19443e7ef4cc495fc7549334a1aff8646d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 24 Mar 2020 06:11:31 -0500 Subject: [PATCH] feat: Replacement support Now can fix typos! Fixes #4 --- Cargo.lock | 101 +++++++++++++++---- Cargo.toml | 3 +- src/args.rs | 4 + src/main.rs | 11 +- src/replace.rs | 240 ++++++++++++++++++++++++++++++++++++++++++++ typos/Cargo.toml | 1 + typos/src/checks.rs | 23 +---- typos/src/report.rs | 134 ++++++++++++++++--------- 8 files changed, 426 insertions(+), 91 deletions(-) create mode 100644 src/replace.rs diff --git a/Cargo.lock b/Cargo.lock index 7d4089a..45052ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,7 +94,7 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", @@ -186,6 +186,41 @@ dependencies = [ "memchr", ] +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.10", + "quote 1.0.3", + "strsim 0.9.3", + "syn 1.0.17", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote 1.0.3", + "syn 1.0.17", +] + [[package]] name = "derive_more" version = "0.15.0" @@ -206,7 +241,19 @@ version = "0.99.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", + "quote 1.0.3", + "syn 1.0.17", +] + +[[package]] +name = "derive_setters" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6604612c19dd3bb353650b715b61f09bcb089dd17bdca1a9a42637079bf5e428" +dependencies = [ + "darling", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", ] @@ -302,9 +349,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" dependencies = [ "libc", ] @@ -318,6 +365,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "ignore" version = "0.4.14" @@ -498,12 +551,12 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", "version_check", @@ -511,11 +564,11 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", "syn-mid", @@ -533,9 +586,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" dependencies = [ "unicode-xid 0.2.0", ] @@ -561,7 +614,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", ] [[package]] @@ -711,7 +764,7 @@ version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", ] @@ -739,6 +792,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + [[package]] name = "structopt" version = "0.3.12" @@ -758,7 +817,7 @@ checksum = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430" dependencies = [ "heck", "proc-macro-error", - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", ] @@ -780,7 +839,7 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "unicode-xid 0.2.0", ] @@ -791,7 +850,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", ] @@ -843,7 +902,7 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227362df41d566be41a28f64401e07a043157c21c14b9785a0d8e256f940a8fd" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.10", "quote 1.0.3", "syn 1.0.17", ] @@ -879,6 +938,7 @@ dependencies = [ "anyhow", "bstr", "derive_more 0.99.5", + "derive_setters", "itertools", "lazy_static", "log", @@ -902,6 +962,7 @@ dependencies = [ "ignore", "log", "phf", + "predicates", "serde", "structopt", "toml", @@ -1032,9 +1093,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" dependencies = [ "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index 6b03741..81612c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,8 @@ serde = { version = "1.0", features = ["derive"] } toml = "0.5" log = "0.4" env_logger = "0.7" +bstr = "0.2" [dev-dependencies] assert_fs = "1.0" -bstr = "0.2" +predicates = "1.0" diff --git a/src/args.rs b/src/args.rs index 6d023b2..4568bdb 100644 --- a/src/args.rs +++ b/src/args.rs @@ -54,6 +54,10 @@ pub(crate) struct Args { /// Ignore implicit configuration files. pub(crate) isolated: bool, + #[structopt(long, short = "w")] + /// Write corrections out + pub(crate) write_changes: bool, + #[structopt(long)] /// Print each file that would be spellchecked. pub(crate) files: bool, diff --git a/src/main.rs b/src/main.rs index 7beed0e..b8b3795 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ mod args; mod checks; mod config; mod dict; +mod replace; fn main() { let code = run().unwrap(); @@ -75,7 +76,11 @@ fn run() -> Result { .git_exclude(config.files.ignore_vcs()) .parents(config.files.ignore_parent()); - let reporter = args.format.reporter(); + let mut reporter = args.format.reporter(); + let replace_reporter = replace::Replace::new(reporter); + if args.write_changes { + reporter = &replace_reporter; + } if args.files { if single_threaded { @@ -149,6 +154,10 @@ fn run() -> Result { errors_found = true; } } + + if args.write_changes { + replace_reporter.write()?; + } } if errors_found { diff --git a/src/replace.rs b/src/replace.rs new file mode 100644 index 0000000..e64d279 --- /dev/null +++ b/src/replace.rs @@ -0,0 +1,240 @@ +use std::collections::BTreeMap; +use std::io::Write; +use std::path; +use std::sync; + +use bstr::ByteSlice; + +pub struct Replace<'r> { + reporter: &'r dyn typos::report::Report, + deferred: sync::Mutex, +} + +impl<'r> Replace<'r> { + pub(crate) fn new(reporter: &'r dyn typos::report::Report) -> Self { + Self { + reporter, + deferred: sync::Mutex::new(Deferred::default()), + } + } + + pub fn write(&self) -> Result<(), std::io::Error> { + let deferred = self.deferred.lock().unwrap(); + + for (path, corrections) in deferred.content.iter() { + let buffer = std::fs::read(path)?; + + let mut file = std::fs::File::create(path)?; + for (line_idx, line) in buffer.lines_with_terminator().enumerate() { + let line_num = line_idx + 1; + if let Some(corrections) = corrections.get(&line_num) { + let line = line.to_vec(); + let line = correct(line, &corrections); + file.write_all(&line)?; + } else { + file.write_all(&line)?; + } + } + } + + for (path, corrections) in deferred.paths.iter() { + let orig_name = path + .file_name() + .and_then(|s| s.to_str()) + .expect("generating a correction requires the filename to be valid.") + .to_owned() + .into_bytes(); + let new_name = correct(orig_name, &corrections); + let new_name = String::from_utf8(new_name).expect("corrections are valid utf-8"); + let new_path = path.with_file_name(new_name); + std::fs::rename(path, new_path)?; + } + + Ok(()) + } +} + +impl<'r> typos::report::Report for Replace<'r> { + fn report(&self, msg: typos::report::Message<'_>) { + match msg { + typos::report::Message::Correction(msg) => { + let path = msg.path.to_owned(); + let line_num = msg.line_num; + let correction = Correction::from_content(msg); + let mut deferred = self.deferred.lock().unwrap(); + let content = deferred + .content + .entry(path) + .or_insert_with(BTreeMap::new) + .entry(line_num) + .or_insert_with(Vec::new); + content.push(correction); + } + typos::report::Message::PathCorrection(msg) => { + let path = msg.path.to_owned(); + let correction = Correction::from_path(msg); + let mut deferred = self.deferred.lock().unwrap(); + let content = deferred.paths.entry(path).or_insert_with(Vec::new); + content.push(correction); + } + _ => self.reporter.report(msg), + } + } +} + +#[derive(Clone, Debug, Default)] +struct Deferred { + content: BTreeMap>>, + paths: BTreeMap>, +} + +#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +struct Correction { + pub byte_offset: usize, + pub typo: Vec, + pub correction: Vec, +} + +impl Correction { + fn from_content(other: typos::report::Correction<'_>) -> Self { + Self { + byte_offset: other.byte_offset, + typo: other.typo.as_bytes().to_vec(), + correction: other.correction.as_bytes().to_vec(), + } + } + + fn from_path(other: typos::report::PathCorrection<'_>) -> Self { + Self { + byte_offset: other.byte_offset, + typo: other.typo.as_bytes().to_vec(), + correction: other.correction.as_bytes().to_vec(), + } + } +} + +fn correct(mut line: Vec, corrections: &[Correction]) -> Vec { + let mut corrections: Vec<_> = corrections.iter().collect(); + corrections.sort_unstable(); + corrections.reverse(); + + for correction in corrections { + let start = correction.byte_offset; + let end = start + correction.typo.len(); + line.splice(start..end, correction.correction.iter().copied()); + } + + line +} + +#[cfg(test)] +mod test { + use super::*; + + use assert_fs::prelude::*; + use typos::report::Report; + + fn simple_correct(line: &str, corrections: Vec<(usize, &str, &str)>) -> String { + let line = line.as_bytes().to_vec(); + let corrections: Vec<_> = corrections + .into_iter() + .map(|(byte_offset, typo, correction)| Correction { + byte_offset, + typo: typo.as_bytes().to_vec(), + correction: correction.as_bytes().to_vec(), + }) + .collect(); + let actual = correct(line, &corrections); + String::from_utf8(actual).unwrap() + } + + #[test] + fn test_correct_single() { + let actual = simple_correct("foo foo foo", vec![(4, "foo", "bar")]); + assert_eq!(actual, "foo bar foo"); + } + + #[test] + fn test_correct_single_grow() { + let actual = simple_correct("foo foo foo", vec![(4, "foo", "happy")]); + assert_eq!(actual, "foo happy foo"); + } + + #[test] + fn test_correct_single_shrink() { + let actual = simple_correct("foo foo foo", vec![(4, "foo", "if")]); + assert_eq!(actual, "foo if foo"); + } + + #[test] + fn test_correct_start() { + let actual = simple_correct("foo foo foo", vec![(0, "foo", "bar")]); + assert_eq!(actual, "bar foo foo"); + } + + #[test] + fn test_correct_end() { + let actual = simple_correct("foo foo foo", vec![(8, "foo", "bar")]); + assert_eq!(actual, "foo foo bar"); + } + + #[test] + fn test_correct_end_grow() { + let actual = simple_correct("foo foo foo", vec![(8, "foo", "happy")]); + assert_eq!(actual, "foo foo happy"); + } + + #[test] + fn test_correct_multiple() { + let actual = simple_correct( + "foo foo foo", + vec![(4, "foo", "happy"), (8, "foo", "world")], + ); + assert_eq!(actual, "foo happy world"); + } + + #[test] + fn test_replace_content() { + let temp = assert_fs::TempDir::new().unwrap(); + let input_file = temp.child("foo.txt"); + input_file.write_str("1 foo 2\n3 4 5").unwrap(); + + let primary = typos::report::PrintSilent; + let replace = Replace::new(&primary); + replace.report( + typos::report::Correction::default() + .path(input_file.path()) + .line(b"1 foo 2\n3 4 5") + .line_num(1) + .byte_offset(2) + .typo("foo") + .correction(std::borrow::Cow::Borrowed("bar")) + .into(), + ); + replace.write().unwrap(); + + input_file.assert("1 bar 2\n3 4 5"); + } + + #[test] + fn test_replace_path() { + let temp = assert_fs::TempDir::new().unwrap(); + let input_file = temp.child("foo.txt"); + input_file.write_str("foo foo foo").unwrap(); + + let primary = typos::report::PrintSilent; + let replace = Replace::new(&primary); + replace.report( + typos::report::PathCorrection::default() + .path(input_file.path()) + .byte_offset(0) + .typo("foo") + .correction(std::borrow::Cow::Borrowed("bar")) + .into(), + ); + replace.write().unwrap(); + + input_file.assert(predicates::path::missing()); + temp.child("bar.txt").assert("foo foo foo"); + } +} diff --git a/typos/Cargo.toml b/typos/Cargo.toml index b258929..eadfcb9 100644 --- a/typos/Cargo.toml +++ b/typos/Cargo.toml @@ -26,3 +26,4 @@ bstr = "0.2" log = "0.4" unicode-segmentation = "1.6.0" derive_more = "0.99.5" +derive_setters = "0.1" diff --git a/typos/src/checks.rs b/typos/src/checks.rs index 6c96d8e..cee181f 100644 --- a/typos/src/checks.rs +++ b/typos/src/checks.rs @@ -91,7 +91,6 @@ impl ParseIdentifiers { path, kind: report::ParseKind::Identifier, data: parser.parse(part).map(|i| i.token()).collect(), - non_exhaustive: (), }; reporter.report(msg.into()); } @@ -115,10 +114,7 @@ impl ParseIdentifiers { let buffer = std::fs::read(path) .map_err(|e| crate::ErrorKind::IoError.into_error().with_source(e))?; if !explicit && !self.binary && is_binary(&buffer) { - let msg = report::BinaryFile { - path, - non_exhaustive: (), - }; + let msg = report::BinaryFile { path }; reporter.report(msg.into()); return Ok(typos_found); } @@ -128,7 +124,6 @@ impl ParseIdentifiers { path, kind: report::ParseKind::Identifier, data: parser.parse_bytes(line).map(|i| i.token()).collect(), - non_exhaustive: (), }; reporter.report(msg.into()); } @@ -165,7 +160,6 @@ impl ParseWords { .parse(part) .flat_map(|ident| ident.split().map(|i| i.token())) .collect(), - non_exhaustive: (), }; reporter.report(msg.into()); } @@ -189,10 +183,7 @@ impl ParseWords { let buffer = std::fs::read(path) .map_err(|e| crate::ErrorKind::IoError.into_error().with_source(e))?; if !explicit && !self.binary && is_binary(&buffer) { - let msg = report::BinaryFile { - path, - non_exhaustive: (), - }; + let msg = report::BinaryFile { path }; reporter.report(msg.into()); return Ok(typos_found); } @@ -205,7 +196,6 @@ impl ParseWords { .parse_bytes(line) .flat_map(|ident| ident.split().map(|i| i.token())) .collect(), - non_exhaustive: (), }; reporter.report(msg.into()); } @@ -244,7 +234,6 @@ impl Checks { byte_offset, typo: ident.token(), correction, - non_exhaustive: (), }; reporter.report(msg.into()); typos_found = true; @@ -257,7 +246,6 @@ impl Checks { byte_offset, typo: word.token(), correction, - non_exhaustive: (), }; reporter.report(msg.into()); typos_found = true; @@ -287,10 +275,7 @@ impl Checks { let buffer = std::fs::read(path) .map_err(|e| crate::ErrorKind::IoError.into_error().with_source(e))?; if !explicit && !self.binary && is_binary(&buffer) { - let msg = report::BinaryFile { - path, - non_exhaustive: (), - }; + let msg = report::BinaryFile { path }; reporter.report(msg.into()); return Ok(typos_found); } @@ -307,7 +292,6 @@ impl Checks { byte_offset, typo: ident.token(), correction, - non_exhaustive: (), }; typos_found = true; reporter.report(msg.into()); @@ -322,7 +306,6 @@ impl Checks { byte_offset, typo: word.token(), correction, - non_exhaustive: (), }; typos_found = true; reporter.report(msg.into()); diff --git a/typos/src/report.rs b/typos/src/report.rs index efd2079..5416525 100644 --- a/typos/src/report.rs +++ b/typos/src/report.rs @@ -4,6 +4,7 @@ use std::io::{self, Write}; #[derive(Clone, Debug, serde::Serialize, derive_more::From)] #[serde(rename_all = "snake_case")] #[serde(tag = "type")] +#[non_exhaustive] pub enum Message<'m> { BinaryFile(BinaryFile<'m>), Correction(Correction<'m>), @@ -12,19 +13,17 @@ pub enum Message<'m> { Parse(Parse<'m>), PathError(PathError<'m>), Error(Error), - #[serde(skip)] - __NonExhaustive, } -#[derive(Clone, Debug, serde::Serialize, derive_more::Display)] +#[derive(Clone, Debug, serde::Serialize, derive_more::Display, derive_setters::Setters)] #[display(fmt = "Skipping binary file {}", "path.display()")] +#[non_exhaustive] pub struct BinaryFile<'m> { pub path: &'m std::path::Path, - #[serde(skip)] - pub(crate) non_exhaustive: (), } -#[derive(Clone, Debug, serde::Serialize)] +#[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] +#[non_exhaustive] pub struct Correction<'m> { pub path: &'m std::path::Path, #[serde(skip)] @@ -33,74 +32,117 @@ pub struct Correction<'m> { pub byte_offset: usize, pub typo: &'m str, pub correction: Cow<'m, str>, - #[serde(skip)] - pub(crate) non_exhaustive: (), } -#[derive(Clone, Debug, serde::Serialize)] +impl<'m> Default for Correction<'m> { + fn default() -> Self { + Self { + path: std::path::Path::new("-"), + line: b"", + line_num: 0, + byte_offset: 0, + typo: "", + correction: Cow::Borrowed(""), + } + } +} + +#[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] +#[non_exhaustive] pub struct PathCorrection<'m> { pub path: &'m std::path::Path, pub byte_offset: usize, pub typo: &'m str, pub correction: Cow<'m, str>, - #[serde(skip)] - pub(crate) non_exhaustive: (), } -#[derive(Copy, Clone, Debug, serde::Serialize)] -pub enum ParseKind { - Identifier, - Word, - #[doc(hidden)] - __NonExhaustive, -} - -#[derive(Clone, Debug, serde::Serialize)] -pub struct File<'m> { - pub path: &'m std::path::Path, - #[serde(skip)] - pub(crate) non_exhaustive: (), -} - -impl<'m> File<'m> { - pub fn new(path: &'m std::path::Path) -> Self { +impl<'m> Default for PathCorrection<'m> { + fn default() -> Self { Self { - path, - non_exhaustive: (), + path: std::path::Path::new("-"), + byte_offset: 0, + typo: "", + correction: Cow::Borrowed(""), } } } -#[derive(Clone, Debug, serde::Serialize)] +#[derive(Copy, Clone, Debug, serde::Serialize)] +#[non_exhaustive] +pub enum ParseKind { + Identifier, + Word, +} + +#[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] +#[non_exhaustive] +pub struct File<'m> { + pub path: &'m std::path::Path, +} + +impl<'m> File<'m> { + pub fn new(path: &'m std::path::Path) -> Self { + Self { path } + } +} + +impl<'m> Default for File<'m> { + fn default() -> Self { + Self { + path: std::path::Path::new("-"), + } + } +} + +#[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] +#[non_exhaustive] pub struct Parse<'m> { pub path: &'m std::path::Path, pub kind: ParseKind, pub data: Vec<&'m str>, - #[serde(skip)] - pub(crate) non_exhaustive: (), } -#[derive(Clone, Debug, serde::Serialize)] +impl<'m> Default for Parse<'m> { + fn default() -> Self { + Self { + path: std::path::Path::new("-"), + kind: ParseKind::Identifier, + data: vec![], + } + } +} + +#[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] +#[non_exhaustive] pub struct PathError<'m> { pub path: &'m std::path::Path, pub msg: String, - #[serde(skip)] - pub(crate) non_exhaustive: (), } -#[derive(Clone, Debug, serde::Serialize)] +impl<'m> Default for PathError<'m> { + fn default() -> Self { + Self { + path: std::path::Path::new("-"), + msg: "".to_owned(), + } + } +} + +#[derive(Clone, Debug, serde::Serialize, derive_setters::Setters)] +#[non_exhaustive] pub struct Error { pub msg: String, - #[serde(skip)] - pub(crate) non_exhaustive: (), } impl Error { pub fn new(msg: String) -> Self { - Self { - msg, - non_exhaustive: (), - } + Self { msg } + } +} + +impl Default for Error { + fn default() -> Self { + Self { msg: "".to_owned() } } } @@ -149,9 +191,6 @@ impl Report for PrintBrief { Message::Error(msg) => { println!("{}", msg.msg); } - Message::__NonExhaustive => { - unreachable!("Non-creatable case"); - } } } } @@ -186,9 +225,6 @@ impl Report for PrintLong { Message::Error(msg) => { println!("{}", msg.msg); } - Message::__NonExhaustive => { - unreachable!("Non-creatable case"); - } } } }