feat: Replacement support

Now can fix typos!

Fixes #4
This commit is contained in:
Ed Page 2020-03-24 06:11:31 -05:00 committed by Ed Page
parent 99450e4b21
commit d1be9c1944
8 changed files with 426 additions and 91 deletions

101
Cargo.lock generated
View file

@ -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",
]

View file

@ -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"

View file

@ -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,

View file

@ -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<i32, anyhow::Error> {
.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<i32, anyhow::Error> {
errors_found = true;
}
}
if args.write_changes {
replace_reporter.write()?;
}
}
if errors_found {

240
src/replace.rs Normal file
View file

@ -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<Deferred>,
}
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<path::PathBuf, BTreeMap<usize, Vec<Correction>>>,
paths: BTreeMap<path::PathBuf, Vec<Correction>>,
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
struct Correction {
pub byte_offset: usize,
pub typo: Vec<u8>,
pub correction: Vec<u8>,
}
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<u8>, corrections: &[Correction]) -> Vec<u8> {
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");
}
}

View file

@ -26,3 +26,4 @@ bstr = "0.2"
log = "0.4"
unicode-segmentation = "1.6.0"
derive_more = "0.99.5"
derive_setters = "0.1"

View file

@ -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());

View file

@ -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");
}
}
}
}