printer: clean-up

Like a previous commit did for the grep-cli crate, this does some
polishing to the grep-printer crate. We aren't able to achieve as much
as we did with grep-cli, but we at least eliminate all rust-analyzer
lints and group imports in the way I've been doing recently.

Next we'll start doing some more invasive changes.
This commit is contained in:
Andrew Gallant 2023-09-21 16:57:02 -04:00
parent 25a7145c79
commit 09905560ff
13 changed files with 291 additions and 258 deletions

5
Cargo.lock generated
View File

@ -19,9 +19,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.20.0" version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@ -220,7 +220,6 @@ dependencies = [
"grep-matcher", "grep-matcher",
"grep-regex", "grep-regex",
"grep-searcher", "grep-searcher",
"lazy_static",
"serde", "serde",
"serde_json", "serde_json",
"termcolor", "termcolor",

View File

@ -12,22 +12,33 @@ repository = "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer"
readme = "README.md" readme = "README.md"
keywords = ["grep", "pattern", "print", "printer", "sink"] keywords = ["grep", "pattern", "print", "printer", "sink"]
license = "Unlicense OR MIT" license = "Unlicense OR MIT"
edition = "2018" edition = "2021"
[features] [features]
default = ["serde1"] default = ["serde"]
serde1 = ["base64", "serde", "serde_json"] serde = ["dep:base64", "dep:serde", "dep:serde_json"]
[dependencies] [dependencies]
base64 = { version = "0.20.0", optional = true } base64 = { version = "0.21.4", optional = true }
bstr = "1.6.0" bstr = "1.6.2"
gethostname = "0.4.3" gethostname = "0.4.3"
grep-matcher = { version = "0.1.6", path = "../matcher" } grep-matcher = { version = "0.1.6", path = "../matcher" }
grep-searcher = { version = "0.1.11", path = "../searcher" } grep-searcher = { version = "0.1.11", path = "../searcher" }
lazy_static = "1.1.0" termcolor = "1.3.0"
termcolor = "1.0.4" serde = { version = "1.0.188", optional = true, features = ["derive"] }
serde = { version = "1.0.77", optional = true, features = ["derive"] } serde_json = { version = "1.0.107", optional = true }
serde_json = { version = "1.0.27", optional = true }
[dev-dependencies] [dev-dependencies]
grep-regex = { version = "0.1.11", path = "../regex" } grep-regex = { version = "0.1.11", path = "../regex" }
[package.metadata.docs.rs]
# We want to document all features.
all-features = true
# This opts into a nightly unstable option to show the features that need to be
# enabled for public API items. To do that, we set 'docsrs', and when that's
# enabled, we enable the 'doc_auto_cfg' feature.
#
# To test this locally, run:
#
# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
rustdoc-args = ["--cfg", "docsrs"]

View File

@ -1,7 +1,3 @@
use std::error;
use std::fmt;
use std::str::FromStr;
use termcolor::{Color, ColorSpec, ParseColorError}; use termcolor::{Color, ColorSpec, ParseColorError};
/// Returns a default set of color specifications. /// Returns a default set of color specifications.
@ -38,17 +34,7 @@ pub enum ColorError {
InvalidFormat(String), InvalidFormat(String),
} }
impl error::Error for ColorError { impl std::error::Error for ColorError {}
fn description(&self) -> &str {
match *self {
ColorError::UnrecognizedOutType(_) => "unrecognized output type",
ColorError::UnrecognizedSpecType(_) => "unrecognized spec type",
ColorError::UnrecognizedColor(_, _) => "unrecognized color name",
ColorError::UnrecognizedStyle(_) => "unrecognized style attribute",
ColorError::InvalidFormat(_) => "invalid color spec",
}
}
}
impl ColorError { impl ColorError {
fn from_parse_error(err: ParseColorError) -> ColorError { fn from_parse_error(err: ParseColorError) -> ColorError {
@ -59,8 +45,8 @@ impl ColorError {
} }
} }
impl fmt::Display for ColorError { impl std::fmt::Display for ColorError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self { match *self {
ColorError::UnrecognizedOutType(ref name) => write!( ColorError::UnrecognizedOutType(ref name) => write!(
f, f,
@ -305,7 +291,7 @@ impl SpecValue {
} }
} }
impl FromStr for UserColorSpec { impl std::str::FromStr for UserColorSpec {
type Err = ColorError; type Err = ColorError;
fn from_str(s: &str) -> Result<UserColorSpec, ColorError> { fn from_str(s: &str) -> Result<UserColorSpec, ColorError> {
@ -345,7 +331,7 @@ impl FromStr for UserColorSpec {
} }
} }
impl FromStr for OutType { impl std::str::FromStr for OutType {
type Err = ColorError; type Err = ColorError;
fn from_str(s: &str) -> Result<OutType, ColorError> { fn from_str(s: &str) -> Result<OutType, ColorError> {
@ -359,7 +345,7 @@ impl FromStr for OutType {
} }
} }
impl FromStr for SpecType { impl std::str::FromStr for SpecType {
type Err = ColorError; type Err = ColorError;
fn from_str(s: &str) -> Result<SpecType, ColorError> { fn from_str(s: &str) -> Result<SpecType, ColorError> {
@ -373,7 +359,7 @@ impl FromStr for SpecType {
} }
} }
impl FromStr for Style { impl std::str::FromStr for Style {
type Err = ColorError; type Err = ColorError;
fn from_str(s: &str) -> Result<Style, ColorError> { fn from_str(s: &str) -> Result<Style, ColorError> {

View File

@ -5,32 +5,32 @@ use termcolor::{ColorSpec, HyperlinkSpec, WriteColor};
/// A writer that counts the number of bytes that have been successfully /// A writer that counts the number of bytes that have been successfully
/// written. /// written.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CounterWriter<W> { pub(crate) struct CounterWriter<W> {
wtr: W, wtr: W,
count: u64, count: u64,
total_count: u64, total_count: u64,
} }
impl<W: Write> CounterWriter<W> { impl<W: Write> CounterWriter<W> {
pub fn new(wtr: W) -> CounterWriter<W> { pub(crate) fn new(wtr: W) -> CounterWriter<W> {
CounterWriter { wtr: wtr, count: 0, total_count: 0 } CounterWriter { wtr, count: 0, total_count: 0 }
} }
} }
impl<W> CounterWriter<W> { impl<W> CounterWriter<W> {
/// Returns the total number of bytes written since construction or the /// Returns the total number of bytes written since construction or the
/// last time `reset` was called. /// last time `reset` was called.
pub fn count(&self) -> u64 { pub(crate) fn count(&self) -> u64 {
self.count self.count
} }
/// Returns the total number of bytes written since construction. /// Returns the total number of bytes written since construction.
pub fn total_count(&self) -> u64 { pub(crate) fn total_count(&self) -> u64 {
self.total_count + self.count self.total_count + self.count
} }
/// Resets the number of bytes written to `0`. /// Resets the number of bytes written to `0`.
pub fn reset_count(&mut self) { pub(crate) fn reset_count(&mut self) {
self.total_count += self.count; self.total_count += self.count;
self.count = 0; self.count = 0;
} }
@ -40,21 +40,21 @@ impl<W> CounterWriter<W> {
/// After this call, the total count of bytes written to the underlying /// After this call, the total count of bytes written to the underlying
/// writer is erased and reset. /// writer is erased and reset.
#[allow(dead_code)] #[allow(dead_code)]
pub fn clear(&mut self) { pub(crate) fn clear(&mut self) {
self.count = 0; self.count = 0;
self.total_count = 0; self.total_count = 0;
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn get_ref(&self) -> &W { pub(crate) fn get_ref(&self) -> &W {
&self.wtr &self.wtr
} }
pub fn get_mut(&mut self) -> &mut W { pub(crate) fn get_mut(&mut self) -> &mut W {
&mut self.wtr &mut self.wtr
} }
pub fn into_inner(self) -> W { pub(crate) fn into_inner(self) -> W {
self.wtr self.wtr
} }
} }

View File

@ -1,12 +1,14 @@
use std::{
io::{self, Write},
path::Path,
};
use {
bstr::ByteSlice,
termcolor::{HyperlinkSpec, WriteColor},
};
use crate::hyperlink_aliases::HYPERLINK_PATTERN_ALIASES; use crate::hyperlink_aliases::HYPERLINK_PATTERN_ALIASES;
use bstr::ByteSlice;
use std::error::Error;
use std::fmt::Display;
use std::io;
use std::io::Write;
use std::path::Path;
use std::str::FromStr;
use termcolor::{HyperlinkSpec, WriteColor};
/// A builder for `HyperlinkPattern`. /// A builder for `HyperlinkPattern`.
/// ///
@ -65,7 +67,8 @@ pub struct HyperlinkValues<'a> {
/// Represents the {file} part of a hyperlink. /// Represents the {file} part of a hyperlink.
/// ///
/// This is the value to use as-is in the hyperlink, converted from an OS file path. /// This is the value to use as-is in the hyperlink, converted from an OS file
/// path.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct HyperlinkPath(Vec<u8>); pub struct HyperlinkPath(Vec<u8>);
@ -231,7 +234,7 @@ impl HyperlinkPattern {
} }
} }
impl FromStr for HyperlinkPattern { impl std::str::FromStr for HyperlinkPattern {
type Err = HyperlinkPatternError; type Err = HyperlinkPatternError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
@ -308,24 +311,31 @@ impl ToString for Part {
} }
} }
impl Display for HyperlinkPatternError { impl std::fmt::Display for HyperlinkPatternError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
HyperlinkPatternError::InvalidSyntax => { HyperlinkPatternError::InvalidSyntax => {
write!(f, "invalid hyperlink pattern syntax") write!(f, "invalid hyperlink pattern syntax")
} }
HyperlinkPatternError::NoFilePlaceholder => { HyperlinkPatternError::NoFilePlaceholder => {
write!(f, "the {{file}} placeholder is required in hyperlink patterns") write!(
f,
"the {{file}} placeholder is required in hyperlink \
patterns",
)
} }
HyperlinkPatternError::NoLinePlaceholder => { HyperlinkPatternError::NoLinePlaceholder => {
write!(f, "the hyperlink pattern contains a {{column}} placeholder, \ write!(
but no {{line}} placeholder is present") f,
"the hyperlink pattern contains a {{column}} placeholder, \
but no {{line}} placeholder is present",
)
} }
HyperlinkPatternError::InvalidPlaceholder(name) => { HyperlinkPatternError::InvalidPlaceholder(name) => {
write!( write!(
f, f,
"invalid hyperlink pattern placeholder: '{}', choose from: \ "invalid hyperlink pattern placeholder: '{}', choose \
file, line, column, host", from: file, line, column, host",
name name
) )
} }
@ -339,7 +349,7 @@ impl Display for HyperlinkPatternError {
} }
} }
impl Error for HyperlinkPatternError {} impl std::error::Error for HyperlinkPatternError {}
impl<'a> HyperlinkValues<'a> { impl<'a> HyperlinkValues<'a> {
/// Creates a new set of hyperlink values. /// Creates a new set of hyperlink values.
@ -360,8 +370,9 @@ impl HyperlinkPath {
/// Returns a hyperlink path from an OS path. /// Returns a hyperlink path from an OS path.
#[cfg(unix)] #[cfg(unix)]
pub fn from_path(path: &Path) -> Option<Self> { pub fn from_path(path: &Path) -> Option<Self> {
// On Unix, this function returns the absolute file path without the leading slash, // On Unix, this function returns the absolute file path without the
// as it makes for more natural hyperlink patterns, for instance: // leading slash, as it makes for more natural hyperlink patterns, for
// instance:
// file://{host}/{file} instead of file://{host}{file} // file://{host}/{file} instead of file://{host}{file}
// vscode://file/{file} instead of vscode://file{file} // vscode://file/{file} instead of vscode://file{file}
// It also allows for patterns to be multi-platform. // It also allows for patterns to be multi-platform.
@ -410,11 +421,12 @@ impl HyperlinkPath {
// Also note that the file://C:/dir/file.txt syntax is not correct, // Also note that the file://C:/dir/file.txt syntax is not correct,
// even though it often works in practice. // even though it often works in practice.
// //
// In the end, this choice was confirmed by VSCode, whose pattern // In the end, this choice was confirmed by VSCode, whose pattern is
// is vscode://file/{file}:{line}:{column} and which correctly understands // vscode://file/{file}:{line}:{column} and which correctly understands
// the following URL format for network drives: // the following URL format for network drives:
// vscode://file//server/dir/file.txt:1:1 // vscode://file//server/dir/file.txt:1:1
// It doesn't parse any other number of slashes in "file//server" as a network path. // It doesn't parse any other number of slashes in "file//server" as a
// network path.
const WIN32_NAMESPACE_PREFIX: &[u8] = br"\\?\"; const WIN32_NAMESPACE_PREFIX: &[u8] = br"\\?\";
const UNC_PREFIX: &[u8] = br"UNC\"; const UNC_PREFIX: &[u8] = br"UNC\";
@ -438,14 +450,15 @@ impl HyperlinkPath {
/// Percent-encodes a path. /// Percent-encodes a path.
/// ///
/// The alphanumeric ASCII characters and "-", ".", "_", "~" are unreserved /// The alphanumeric ASCII characters and "-", ".", "_", "~" are unreserved
/// as per section 2.3 of RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax), /// as per section 2.3 of RFC 3986 (Uniform Resource Identifier (URI):
/// and are not encoded. The other ASCII characters except "/" and ":" are percent-encoded, /// Generic Syntax), and are not encoded. The other ASCII characters except
/// and "\" is replaced by "/" on Windows. /// "/" and ":" are percent-encoded, and "\" is replaced by "/" on Windows.
/// ///
/// Section 4 of RFC 8089 (The "file" URI Scheme) does not mandate precise encoding /// Section 4 of RFC 8089 (The "file" URI Scheme) does not mandate precise
/// requirements for non-ASCII characters, and this implementation leaves them unencoded. /// encoding requirements for non-ASCII characters, and this implementation
/// On Windows, the UrlCreateFromPathW function does not encode non-ASCII characters. /// leaves them unencoded. On Windows, the UrlCreateFromPathW function does
/// Doing so with UTF-8 encoded paths creates invalid file:// URLs on that platform. /// not encode non-ASCII characters. Doing so with UTF-8 encoded paths
/// creates invalid file:// URLs on that platform.
fn encode(input: &[u8]) -> HyperlinkPath { fn encode(input: &[u8]) -> HyperlinkPath {
let mut result = Vec::with_capacity(input.len()); let mut result = Vec::with_capacity(input.len());
@ -480,7 +493,7 @@ impl HyperlinkPath {
} }
} }
impl Display for HyperlinkPath { impl std::fmt::Display for HyperlinkPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@ -490,15 +503,16 @@ impl Display for HyperlinkPath {
} }
} }
/// A simple abstraction over a hyperlink span written to the terminal. /// A simple abstraction over a hyperlink span written to the terminal. This
/// This helps tracking whether a hyperlink has been started, and should be ended. /// helps tracking whether a hyperlink has been started, and should be ended.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct HyperlinkSpan { pub struct HyperlinkSpan {
active: bool, active: bool,
} }
impl HyperlinkSpan { impl HyperlinkSpan {
/// Starts a hyperlink and returns a span which tracks whether it is still in effect. /// Starts a hyperlink and returns a span which tracks whether it is still
/// in effect.
pub fn start( pub fn start(
wtr: &mut impl WriteColor, wtr: &mut impl WriteColor,
hyperlink: &HyperlinkSpec, hyperlink: &HyperlinkSpec,
@ -528,6 +542,8 @@ impl HyperlinkSpan {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr;
use super::*; use super::*;
#[test] #[test]
@ -653,7 +669,8 @@ mod tests {
for name in names { for name in names {
assert!( assert!(
name > previous_name, name > previous_name,
r#""{}" should be sorted before "{}" in `HYPERLINK_PATTERN_ALIASES`"#, "'{}' should be sorted before '{}' \
in HYPERLINK_PATTERN_ALIASES",
name, name,
previous_name previous_name
); );

View File

@ -1,7 +1,7 @@
/// Aliases to well-known hyperlink schemes. /// Aliases to well-known hyperlink schemes.
/// ///
/// These need to be sorted by name. /// These need to be sorted by name.
pub const HYPERLINK_PATTERN_ALIASES: &[(&str, &str)] = &[ pub(crate) const HYPERLINK_PATTERN_ALIASES: &[(&str, &str)] = &[
#[cfg(unix)] #[cfg(unix)]
("file", "file://{host}/{file}"), ("file", "file://{host}/{file}"),
#[cfg(windows)] #[cfg(windows)]

View File

@ -1,17 +1,20 @@
use std::io::{self, Write}; use std::{
use std::path::Path; io::{self, Write},
use std::time::Instant; path::Path,
time::Instant,
use grep_matcher::{Match, Matcher};
use grep_searcher::{
Searcher, Sink, SinkContext, SinkContextKind, SinkFinish, SinkMatch,
}; };
use serde_json as json;
use crate::counter::CounterWriter; use {
use crate::jsont; grep_matcher::{Match, Matcher},
use crate::stats::Stats; grep_searcher::{
use crate::util::find_iter_at_in_context; Searcher, Sink, SinkContext, SinkContextKind, SinkFinish, SinkMatch,
},
serde_json as json,
};
use crate::{
counter::CounterWriter, jsont, stats::Stats, util::find_iter_at_in_context,
};
/// The configuration for the JSON printer. /// The configuration for the JSON printer.
/// ///
@ -467,7 +470,7 @@ impl<W: io::Write> JSON<W> {
matcher: M, matcher: M,
) -> JSONSink<'static, 's, M, W> { ) -> JSONSink<'static, 's, M, W> {
JSONSink { JSONSink {
matcher: matcher, matcher,
json: self, json: self,
path: None, path: None,
start_time: Instant::now(), start_time: Instant::now(),
@ -493,7 +496,7 @@ impl<W: io::Write> JSON<W> {
P: ?Sized + AsRef<Path>, P: ?Sized + AsRef<Path>,
{ {
JSONSink { JSONSink {
matcher: matcher, matcher,
json: self, json: self,
path: Some(path.as_ref()), path: Some(path.as_ref()),
start_time: Instant::now(), start_time: Instant::now(),

View File

@ -6,19 +6,19 @@
// convenient for deserialization however, so these types would become a bit // convenient for deserialization however, so these types would become a bit
// more complex. // more complex.
use std::borrow::Cow; use std::{borrow::Cow, path::Path};
use std::path::Path;
use std::str;
use base64; use {
use serde::{Serialize, Serializer}; base64,
serde::{Serialize, Serializer},
};
use crate::stats::Stats; use crate::stats::Stats;
#[derive(Serialize)] #[derive(Serialize)]
#[serde(tag = "type", content = "data")] #[serde(tag = "type", content = "data")]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum Message<'a> { pub(crate) enum Message<'a> {
Begin(Begin<'a>), Begin(Begin<'a>),
End(End<'a>), End(End<'a>),
Match(Match<'a>), Match(Match<'a>),
@ -26,48 +26,48 @@ pub enum Message<'a> {
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct Begin<'a> { pub(crate) struct Begin<'a> {
#[serde(serialize_with = "ser_path")] #[serde(serialize_with = "ser_path")]
pub path: Option<&'a Path>, pub(crate) path: Option<&'a Path>,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct End<'a> { pub(crate) struct End<'a> {
#[serde(serialize_with = "ser_path")] #[serde(serialize_with = "ser_path")]
pub path: Option<&'a Path>, pub(crate) path: Option<&'a Path>,
pub binary_offset: Option<u64>, pub(crate) binary_offset: Option<u64>,
pub stats: Stats, pub(crate) stats: Stats,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct Match<'a> { pub(crate) struct Match<'a> {
#[serde(serialize_with = "ser_path")] #[serde(serialize_with = "ser_path")]
pub path: Option<&'a Path>, pub(crate) path: Option<&'a Path>,
#[serde(serialize_with = "ser_bytes")] #[serde(serialize_with = "ser_bytes")]
pub lines: &'a [u8], pub(crate) lines: &'a [u8],
pub line_number: Option<u64>, pub(crate) line_number: Option<u64>,
pub absolute_offset: u64, pub(crate) absolute_offset: u64,
pub submatches: &'a [SubMatch<'a>], pub(crate) submatches: &'a [SubMatch<'a>],
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct Context<'a> { pub(crate) struct Context<'a> {
#[serde(serialize_with = "ser_path")] #[serde(serialize_with = "ser_path")]
pub path: Option<&'a Path>, pub(crate) path: Option<&'a Path>,
#[serde(serialize_with = "ser_bytes")] #[serde(serialize_with = "ser_bytes")]
pub lines: &'a [u8], pub(crate) lines: &'a [u8],
pub line_number: Option<u64>, pub(crate) line_number: Option<u64>,
pub absolute_offset: u64, pub(crate) absolute_offset: u64,
pub submatches: &'a [SubMatch<'a>], pub(crate) submatches: &'a [SubMatch<'a>],
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct SubMatch<'a> { pub(crate) struct SubMatch<'a> {
#[serde(rename = "match")] #[serde(rename = "match")]
#[serde(serialize_with = "ser_bytes")] #[serde(serialize_with = "ser_bytes")]
pub m: &'a [u8], pub(crate) m: &'a [u8],
pub start: usize, pub(crate) start: usize,
pub end: usize, pub(crate) end: usize,
} }
/// Data represents things that look like strings, but may actually not be /// Data represents things that look like strings, but may actually not be
@ -91,7 +91,7 @@ enum Data<'a> {
impl<'a> Data<'a> { impl<'a> Data<'a> {
fn from_bytes(bytes: &[u8]) -> Data<'_> { fn from_bytes(bytes: &[u8]) -> Data<'_> {
match str::from_utf8(bytes) { match std::str::from_utf8(bytes) {
Ok(text) => Data::Text { text: Cow::Borrowed(text) }, Ok(text) => Data::Text { text: Cow::Borrowed(text) },
Err(_) => Data::Bytes { bytes }, Err(_) => Data::Bytes { bytes },
} }
@ -123,7 +123,8 @@ where
T: AsRef<[u8]>, T: AsRef<[u8]>,
S: Serializer, S: Serializer,
{ {
ser.serialize_str(&base64::encode(&bytes)) use base64::engine::{general_purpose::STANDARD, Engine};
ser.serialize_str(&STANDARD.encode(&bytes))
} }
fn ser_bytes<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error> fn ser_bytes<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error>

View File

@ -27,11 +27,11 @@ contain matches.
This example shows how to create a "standard" printer and execute a search. This example shows how to create a "standard" printer and execute a search.
``` ```
use std::error::Error; use {
grep_regex::RegexMatcher,
use grep_regex::RegexMatcher; grep_printer::Standard,
use grep_printer::Standard; grep_searcher::Searcher,
use grep_searcher::Searcher; };
const SHERLOCK: &'static [u8] = b"\ const SHERLOCK: &'static [u8] = b"\
For the Doctor Watsons of this world, as opposed to the Sherlock For the Doctor Watsons of this world, as opposed to the Sherlock
@ -42,8 +42,6 @@ but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached. and exhibited clearly, with a label attached.
"; ";
# fn main() { example().unwrap(); }
fn example() -> Result<(), Box<Error>> {
let matcher = RegexMatcher::new(r"Sherlock")?; let matcher = RegexMatcher::new(r"Sherlock")?;
let mut printer = Standard::new_no_color(vec![]); let mut printer = Standard::new_no_color(vec![]);
Searcher::new().search_slice(&matcher, SHERLOCK, printer.sink(&matcher))?; Searcher::new().search_slice(&matcher, SHERLOCK, printer.sink(&matcher))?;
@ -57,26 +55,27 @@ fn example() -> Result<(), Box<Error>> {
3:be, to a very large extent, the result of luck. Sherlock Holmes 3:be, to a very large extent, the result of luck. Sherlock Holmes
"; ";
assert_eq!(output, expected); assert_eq!(output, expected);
Ok(()) # Ok::<(), Box<dyn std::error::Error>>(())
}
``` ```
*/ */
#![deny(missing_docs)] #![deny(missing_docs)]
#![cfg_attr(feature = "pattern", feature(pattern))]
pub use crate::color::{ pub use crate::{
default_color_specs, ColorError, ColorSpecs, UserColorSpec, color::{default_color_specs, ColorError, ColorSpecs, UserColorSpec},
hyperlink::{
HyperlinkPath, HyperlinkPattern, HyperlinkPatternBuilder,
HyperlinkPatternError, HyperlinkSpan, HyperlinkValues,
},
standard::{Standard, StandardBuilder, StandardSink},
stats::Stats,
summary::{Summary, SummaryBuilder, SummaryKind, SummarySink},
util::PrinterPath,
}; };
pub use crate::hyperlink::{
HyperlinkPath, HyperlinkPattern, HyperlinkPatternError, HyperlinkSpan, #[cfg(feature = "serde")]
HyperlinkValues,
};
#[cfg(feature = "serde1")]
pub use crate::json::{JSONBuilder, JSONSink, JSON}; pub use crate::json::{JSONBuilder, JSONSink, JSON};
pub use crate::standard::{Standard, StandardBuilder, StandardSink};
pub use crate::stats::Stats;
pub use crate::summary::{Summary, SummaryBuilder, SummaryKind, SummarySink};
pub use crate::util::PrinterPath;
// The maximum number of bytes to execute a search to account for look-ahead. // The maximum number of bytes to execute a search to account for look-ahead.
// //
@ -96,9 +95,9 @@ mod color;
mod counter; mod counter;
mod hyperlink; mod hyperlink;
mod hyperlink_aliases; mod hyperlink_aliases;
#[cfg(feature = "serde1")] #[cfg(feature = "serde")]
mod json; mod json;
#[cfg(feature = "serde1")] #[cfg(feature = "serde")]
mod jsont; mod jsont;
mod standard; mod standard;
mod stats; mod stats;

View File

@ -1,25 +1,31 @@
use std::cell::{Cell, RefCell}; use std::{
use std::cmp; cell::{Cell, RefCell},
use std::io::{self, Write}; cmp,
use std::path::Path; io::{self, Write},
use std::sync::Arc; path::Path,
use std::time::Instant; sync::Arc,
time::Instant,
};
use bstr::ByteSlice; use {
use grep_matcher::{Match, Matcher}; bstr::ByteSlice,
use grep_searcher::{ grep_matcher::{Match, Matcher},
grep_searcher::{
LineStep, Searcher, Sink, SinkContext, SinkContextKind, SinkFinish, LineStep, Searcher, Sink, SinkContext, SinkContextKind, SinkFinish,
SinkMatch, SinkMatch,
},
termcolor::{ColorSpec, NoColor, WriteColor},
}; };
use termcolor::{ColorSpec, NoColor, WriteColor};
use crate::color::ColorSpecs; use crate::{
use crate::counter::CounterWriter; color::ColorSpecs,
use crate::hyperlink::{HyperlinkPattern, HyperlinkSpan}; counter::CounterWriter,
use crate::stats::Stats; hyperlink::{HyperlinkPattern, HyperlinkSpan},
use crate::util::{ stats::Stats,
util::{
find_iter_at_in_context, trim_ascii_prefix, trim_line_terminator, find_iter_at_in_context, trim_ascii_prefix, trim_line_terminator,
PrinterPath, Replacer, Sunk, PrinterPath, Replacer, Sunk,
},
}; };
/// The configuration for the standard printer. /// The configuration for the standard printer.
@ -522,7 +528,7 @@ impl<W: WriteColor> Standard<W> {
let stats = if self.config.stats { Some(Stats::new()) } else { None }; let stats = if self.config.stats { Some(Stats::new()) } else { None };
let needs_match_granularity = self.needs_match_granularity(); let needs_match_granularity = self.needs_match_granularity();
StandardSink { StandardSink {
matcher: matcher, matcher,
standard: self, standard: self,
replacer: Replacer::new(), replacer: Replacer::new(),
path: None, path: None,
@ -530,8 +536,8 @@ impl<W: WriteColor> Standard<W> {
match_count: 0, match_count: 0,
after_context_remaining: 0, after_context_remaining: 0,
binary_byte_offset: None, binary_byte_offset: None,
stats: stats, stats,
needs_match_granularity: needs_match_granularity, needs_match_granularity,
} }
} }
@ -558,7 +564,7 @@ impl<W: WriteColor> Standard<W> {
); );
let needs_match_granularity = self.needs_match_granularity(); let needs_match_granularity = self.needs_match_granularity();
StandardSink { StandardSink {
matcher: matcher, matcher,
standard: self, standard: self,
replacer: Replacer::new(), replacer: Replacer::new(),
path: Some(ppath), path: Some(ppath),
@ -566,8 +572,8 @@ impl<W: WriteColor> Standard<W> {
match_count: 0, match_count: 0,
after_context_remaining: 0, after_context_remaining: 0,
binary_byte_offset: None, binary_byte_offset: None,
stats: stats, stats,
needs_match_granularity: needs_match_granularity, needs_match_granularity,
} }
} }
@ -935,8 +941,8 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
sink: &'a StandardSink<'_, '_, M, W>, sink: &'a StandardSink<'_, '_, M, W>,
) -> StandardImpl<'a, M, W> { ) -> StandardImpl<'a, M, W> {
StandardImpl { StandardImpl {
searcher: searcher, searcher,
sink: sink, sink,
sunk: Sunk::empty(), sunk: Sunk::empty(),
in_color_match: Cell::new(false), in_color_match: Cell::new(false),
} }
@ -954,7 +960,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
&sink.standard.matches, &sink.standard.matches,
sink.replacer.replacement(), sink.replacer.replacement(),
); );
StandardImpl { sunk: sunk, ..StandardImpl::new(searcher, sink) } StandardImpl { sunk, ..StandardImpl::new(searcher, sink) }
} }
/// Bundle self with a searcher and return the core implementation of Sink /// Bundle self with a searcher and return the core implementation of Sink
@ -969,7 +975,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
&sink.standard.matches, &sink.standard.matches,
sink.replacer.replacement(), sink.replacer.replacement(),
); );
StandardImpl { sunk: sunk, ..StandardImpl::new(searcher, sink) } StandardImpl { sunk, ..StandardImpl::new(searcher, sink) }
} }
fn sink(&self) -> io::Result<()> { fn sink(&self) -> io::Result<()> {
@ -1657,9 +1663,10 @@ impl<'a, M: Matcher, W: WriteColor> PreludeWriter<'a, M, W> {
/// Starts the prelude with a hyperlink when applicable. /// Starts the prelude with a hyperlink when applicable.
/// ///
/// If a heading was written, and the hyperlink pattern is invariant on the line number, /// If a heading was written, and the hyperlink pattern is invariant on
/// then this doesn't hyperlink each line prelude, as it wouldn't point to the line anyway. /// the line number, then this doesn't hyperlink each line prelude, as it
/// The hyperlink on the heading should be sufficient and less confusing. /// wouldn't point to the line anyway. The hyperlink on the heading should
/// be sufficient and less confusing.
fn start( fn start(
&mut self, &mut self,
line_number: Option<u64>, line_number: Option<u64>,

View File

@ -1,5 +1,7 @@
use std::ops::{Add, AddAssign}; use std::{
use std::time::Duration; ops::{Add, AddAssign},
time::Duration,
};
use crate::util::NiceDuration; use crate::util::NiceDuration;
@ -8,7 +10,7 @@ use crate::util::NiceDuration;
/// When statistics are reported by a printer, they correspond to all searches /// When statistics are reported by a printer, they correspond to all searches
/// executed with that printer. /// executed with that printer.
#[derive(Clone, Debug, Default, PartialEq, Eq)] #[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Stats { pub struct Stats {
elapsed: NiceDuration, elapsed: NiceDuration,
searches: u64, searches: u64,

View File

@ -1,18 +1,24 @@
use std::cell::RefCell; use std::{
use std::io::{self, Write}; cell::RefCell,
use std::path::Path; io::{self, Write},
use std::sync::Arc; path::Path,
use std::time::Instant; sync::Arc,
time::Instant,
};
use grep_matcher::Matcher; use {
use grep_searcher::{Searcher, Sink, SinkError, SinkFinish, SinkMatch}; grep_matcher::Matcher,
use termcolor::{ColorSpec, NoColor, WriteColor}; grep_searcher::{Searcher, Sink, SinkError, SinkFinish, SinkMatch},
termcolor::{ColorSpec, NoColor, WriteColor},
};
use crate::color::ColorSpecs; use crate::{
use crate::counter::CounterWriter; color::ColorSpecs,
use crate::hyperlink::{HyperlinkPattern, HyperlinkSpan}; counter::CounterWriter,
use crate::stats::Stats; hyperlink::{HyperlinkPattern, HyperlinkSpan},
use crate::util::{find_iter_at_in_context, PrinterPath}; stats::Stats,
util::{find_iter_at_in_context, PrinterPath},
};
/// The configuration for the summary printer. /// The configuration for the summary printer.
/// ///
@ -392,13 +398,13 @@ impl<W: WriteColor> Summary<W> {
None None
}; };
SummarySink { SummarySink {
matcher: matcher, matcher,
summary: self, summary: self,
path: None, path: None,
start_time: Instant::now(), start_time: Instant::now(),
match_count: 0, match_count: 0,
binary_byte_offset: None, binary_byte_offset: None,
stats: stats, stats,
} }
} }
@ -428,13 +434,13 @@ impl<W: WriteColor> Summary<W> {
self.config.separator_path, self.config.separator_path,
); );
SummarySink { SummarySink {
matcher: matcher, matcher,
summary: self, summary: self,
path: Some(ppath), path: Some(ppath),
start_time: Instant::now(), start_time: Instant::now(),
match_count: 0, match_count: 0,
binary_byte_offset: None, binary_byte_offset: None,
stats: stats, stats,
} }
} }
} }

View File

@ -1,23 +1,24 @@
use std::borrow::Cow; use std::{borrow::Cow, fmt, io, path::Path, time};
use std::cell::OnceCell;
use std::path::Path;
use std::time;
use std::{fmt, io};
use bstr::{ByteSlice, ByteVec}; use {
use grep_matcher::{Captures, LineTerminator, Match, Matcher}; bstr::{ByteSlice, ByteVec},
use grep_searcher::{ grep_matcher::{Captures, LineTerminator, Match, Matcher},
grep_searcher::{
LineIter, Searcher, SinkContext, SinkContextKind, SinkError, SinkMatch, LineIter, Searcher, SinkContext, SinkContextKind, SinkError, SinkMatch,
},
termcolor::HyperlinkSpec,
}; };
#[cfg(feature = "serde1")]
use serde::{Serialize, Serializer};
use termcolor::HyperlinkSpec;
use crate::hyperlink::{HyperlinkPath, HyperlinkPattern, HyperlinkValues}; #[cfg(feature = "serde")]
use crate::MAX_LOOK_AHEAD; use serde::{Serialize, Serializer};
use crate::{
hyperlink::{HyperlinkPath, HyperlinkPattern, HyperlinkValues},
MAX_LOOK_AHEAD,
};
/// A type for handling replacements while amortizing allocation. /// A type for handling replacements while amortizing allocation.
pub struct Replacer<M: Matcher> { pub(crate) struct Replacer<M: Matcher> {
space: Option<Space<M>>, space: Option<Space<M>>,
} }
@ -45,7 +46,7 @@ impl<M: Matcher> Replacer<M> {
/// ///
/// This constructor does not allocate. Instead, space for dealing with /// This constructor does not allocate. Instead, space for dealing with
/// replacements is allocated lazily only when needed. /// replacements is allocated lazily only when needed.
pub fn new() -> Replacer<M> { pub(crate) fn new() -> Replacer<M> {
Replacer { space: None } Replacer { space: None }
} }
@ -54,7 +55,7 @@ impl<M: Matcher> Replacer<M> {
/// replacement, use the `replacement` method. /// replacement, use the `replacement` method.
/// ///
/// This can fail if the underlying matcher reports an error. /// This can fail if the underlying matcher reports an error.
pub fn replace_all<'a>( pub(crate) fn replace_all<'a>(
&'a mut self, &'a mut self,
searcher: &Searcher, searcher: &Searcher,
matcher: &M, matcher: &M,
@ -112,7 +113,9 @@ impl<M: Matcher> Replacer<M> {
/// all replacement occurrences within the returned replacement buffer. /// all replacement occurrences within the returned replacement buffer.
/// ///
/// If no replacement has occurred then `None` is returned. /// If no replacement has occurred then `None` is returned.
pub fn replacement<'a>(&'a self) -> Option<(&'a [u8], &'a [Match])> { pub(crate) fn replacement<'a>(
&'a self,
) -> Option<(&'a [u8], &'a [Match])> {
match self.space { match self.space {
None => None, None => None,
Some(ref space) => { Some(ref space) => {
@ -129,7 +132,7 @@ impl<M: Matcher> Replacer<M> {
/// ///
/// Subsequent calls to `replacement` after calling `clear` (but before /// Subsequent calls to `replacement` after calling `clear` (but before
/// executing another replacement) will always return `None`. /// executing another replacement) will always return `None`.
pub fn clear(&mut self) { pub(crate) fn clear(&mut self) {
if let Some(ref mut space) = self.space { if let Some(ref mut space) = self.space {
space.dst.clear(); space.dst.clear();
space.matches.clear(); space.matches.clear();
@ -145,8 +148,7 @@ impl<M: Matcher> Replacer<M> {
if self.space.is_none() { if self.space.is_none() {
let caps = let caps =
matcher.new_captures().map_err(io::Error::error_message)?; matcher.new_captures().map_err(io::Error::error_message)?;
self.space = self.space = Some(Space { caps, dst: vec![], matches: vec![] });
Some(Space { caps: caps, dst: vec![], matches: vec![] });
} }
Ok(self.space.as_mut().unwrap()) Ok(self.space.as_mut().unwrap())
} }
@ -165,7 +167,7 @@ impl<M: Matcher> Replacer<M> {
/// results of the replacement instead of the bytes reported directly by the /// results of the replacement instead of the bytes reported directly by the
/// searcher. /// searcher.
#[derive(Debug)] #[derive(Debug)]
pub struct Sunk<'a> { pub(crate) struct Sunk<'a> {
bytes: &'a [u8], bytes: &'a [u8],
absolute_byte_offset: u64, absolute_byte_offset: u64,
line_number: Option<u64>, line_number: Option<u64>,
@ -176,7 +178,7 @@ pub struct Sunk<'a> {
impl<'a> Sunk<'a> { impl<'a> Sunk<'a> {
#[inline] #[inline]
pub fn empty() -> Sunk<'static> { pub(crate) fn empty() -> Sunk<'static> {
Sunk { Sunk {
bytes: &[], bytes: &[],
absolute_byte_offset: 0, absolute_byte_offset: 0,
@ -188,7 +190,7 @@ impl<'a> Sunk<'a> {
} }
#[inline] #[inline]
pub fn from_sink_match( pub(crate) fn from_sink_match(
sunk: &'a SinkMatch<'a>, sunk: &'a SinkMatch<'a>,
original_matches: &'a [Match], original_matches: &'a [Match],
replacement: Option<(&'a [u8], &'a [Match])>, replacement: Option<(&'a [u8], &'a [Match])>,
@ -196,17 +198,17 @@ impl<'a> Sunk<'a> {
let (bytes, matches) = let (bytes, matches) =
replacement.unwrap_or_else(|| (sunk.bytes(), original_matches)); replacement.unwrap_or_else(|| (sunk.bytes(), original_matches));
Sunk { Sunk {
bytes: bytes, bytes,
absolute_byte_offset: sunk.absolute_byte_offset(), absolute_byte_offset: sunk.absolute_byte_offset(),
line_number: sunk.line_number(), line_number: sunk.line_number(),
context_kind: None, context_kind: None,
matches: matches, matches,
original_matches: original_matches, original_matches,
} }
} }
#[inline] #[inline]
pub fn from_sink_context( pub(crate) fn from_sink_context(
sunk: &'a SinkContext<'a>, sunk: &'a SinkContext<'a>,
original_matches: &'a [Match], original_matches: &'a [Match],
replacement: Option<(&'a [u8], &'a [Match])>, replacement: Option<(&'a [u8], &'a [Match])>,
@ -214,47 +216,47 @@ impl<'a> Sunk<'a> {
let (bytes, matches) = let (bytes, matches) =
replacement.unwrap_or_else(|| (sunk.bytes(), original_matches)); replacement.unwrap_or_else(|| (sunk.bytes(), original_matches));
Sunk { Sunk {
bytes: bytes, bytes,
absolute_byte_offset: sunk.absolute_byte_offset(), absolute_byte_offset: sunk.absolute_byte_offset(),
line_number: sunk.line_number(), line_number: sunk.line_number(),
context_kind: Some(sunk.kind()), context_kind: Some(sunk.kind()),
matches: matches, matches,
original_matches: original_matches, original_matches,
} }
} }
#[inline] #[inline]
pub fn context_kind(&self) -> Option<&'a SinkContextKind> { pub(crate) fn context_kind(&self) -> Option<&'a SinkContextKind> {
self.context_kind self.context_kind
} }
#[inline] #[inline]
pub fn bytes(&self) -> &'a [u8] { pub(crate) fn bytes(&self) -> &'a [u8] {
self.bytes self.bytes
} }
#[inline] #[inline]
pub fn matches(&self) -> &'a [Match] { pub(crate) fn matches(&self) -> &'a [Match] {
self.matches self.matches
} }
#[inline] #[inline]
pub fn original_matches(&self) -> &'a [Match] { pub(crate) fn original_matches(&self) -> &'a [Match] {
self.original_matches self.original_matches
} }
#[inline] #[inline]
pub fn lines(&self, line_term: u8) -> LineIter<'a> { pub(crate) fn lines(&self, line_term: u8) -> LineIter<'a> {
LineIter::new(line_term, self.bytes()) LineIter::new(line_term, self.bytes())
} }
#[inline] #[inline]
pub fn absolute_byte_offset(&self) -> u64 { pub(crate) fn absolute_byte_offset(&self) -> u64 {
self.absolute_byte_offset self.absolute_byte_offset
} }
#[inline] #[inline]
pub fn line_number(&self) -> Option<u64> { pub(crate) fn line_number(&self) -> Option<u64> {
self.line_number self.line_number
} }
} }
@ -281,7 +283,7 @@ impl<'a> Sunk<'a> {
pub struct PrinterPath<'a> { pub struct PrinterPath<'a> {
path: &'a Path, path: &'a Path,
bytes: Cow<'a, [u8]>, bytes: Cow<'a, [u8]>,
hyperlink_path: OnceCell<Option<HyperlinkPath>>, hyperlink_path: std::cell::OnceCell<Option<HyperlinkPath>>,
} }
impl<'a> PrinterPath<'a> { impl<'a> PrinterPath<'a> {
@ -290,7 +292,7 @@ impl<'a> PrinterPath<'a> {
PrinterPath { PrinterPath {
path, path,
bytes: Vec::from_path_lossy(path), bytes: Vec::from_path_lossy(path),
hyperlink_path: OnceCell::new(), hyperlink_path: std::cell::OnceCell::new(),
} }
} }
@ -331,8 +333,8 @@ impl<'a> PrinterPath<'a> {
&self.bytes &self.bytes
} }
/// Creates a hyperlink for this path and the given line and column, using the specified /// Creates a hyperlink for this path and the given line and column, using
/// pattern. Uses the given buffer to store the hyperlink. /// the specified pattern. Uses the given buffer to store the hyperlink.
pub fn create_hyperlink_spec<'b>( pub fn create_hyperlink_spec<'b>(
&self, &self,
pattern: &HyperlinkPattern, pattern: &HyperlinkPattern,
@ -365,7 +367,7 @@ impl<'a> PrinterPath<'a> {
/// with the Deserialize impl for std::time::Duration, since this type only /// with the Deserialize impl for std::time::Duration, since this type only
/// adds new fields. /// adds new fields.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct NiceDuration(pub time::Duration); pub(crate) struct NiceDuration(pub time::Duration);
impl fmt::Display for NiceDuration { impl fmt::Display for NiceDuration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -383,7 +385,7 @@ impl NiceDuration {
} }
} }
#[cfg(feature = "serde1")] #[cfg(feature = "serde")]
impl Serialize for NiceDuration { impl Serialize for NiceDuration {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> { fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
@ -401,7 +403,7 @@ impl Serialize for NiceDuration {
/// ///
/// This stops trimming a prefix as soon as it sees non-whitespace or a line /// This stops trimming a prefix as soon as it sees non-whitespace or a line
/// terminator. /// terminator.
pub fn trim_ascii_prefix( pub(crate) fn trim_ascii_prefix(
line_term: LineTerminator, line_term: LineTerminator,
slice: &[u8], slice: &[u8],
range: Match, range: Match,
@ -422,7 +424,7 @@ pub fn trim_ascii_prefix(
range.with_start(range.start() + count) range.with_start(range.start() + count)
} }
pub fn find_iter_at_in_context<M, F>( pub(crate) fn find_iter_at_in_context<M, F>(
searcher: &Searcher, searcher: &Searcher,
matcher: M, matcher: M,
mut bytes: &[u8], mut bytes: &[u8],
@ -482,7 +484,7 @@ where
/// Given a buf and some bounds, if there is a line terminator at the end of /// Given a buf and some bounds, if there is a line terminator at the end of
/// the given bounds in buf, then the bounds are trimmed to remove the line /// the given bounds in buf, then the bounds are trimmed to remove the line
/// terminator. /// terminator.
pub fn trim_line_terminator( pub(crate) fn trim_line_terminator(
searcher: &Searcher, searcher: &Searcher,
buf: &[u8], buf: &[u8],
line: &mut Match, line: &mut Match,