mirror of
https://github.com/Automattic/harper.git
synced 2025-08-04 18:48:02 +00:00
feat(stats): use JSON rows
This commit is contained in:
parent
264c2c23a8
commit
f96a06eaed
7 changed files with 105 additions and 35 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -399,27 +399,6 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
|
@ -837,9 +816,11 @@ dependencies = [
|
|||
name = "harper-stats"
|
||||
version = "0.25.0"
|
||||
dependencies = [
|
||||
"csv",
|
||||
"harper-core",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ fn main() -> anyhow::Result<()> {
|
|||
Args::SummarizeLintRecord { file } => {
|
||||
let file = File::open(file)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let stats = Stats::read_csv(&mut reader)?;
|
||||
let stats = Stats::read(&mut reader)?;
|
||||
|
||||
let summary = stats.summarize();
|
||||
println!("{summary}");
|
||||
|
|
|
@ -123,7 +123,7 @@ impl Backend {
|
|||
.create(true)
|
||||
.open(&config.stats_path)?,
|
||||
);
|
||||
stats.write_csv(&mut writer)?;
|
||||
stats.write(&mut writer)?;
|
||||
writer.flush()?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -7,4 +7,8 @@ edition = "2021"
|
|||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
harper-core = { path = "../harper-core", version = "0.25.0", features = ["concurrent"] }
|
||||
uuid = { version = "1.12.0", features = ["serde", "v4"] }
|
||||
csv = "1.3.1"
|
||||
serde_json = "1.0.140"
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "1.0.3"
|
||||
quickcheck_macros = "1.0.0"
|
||||
|
|
3
harper-stats/README.md
Normal file
3
harper-stats/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# `harper-stats`
|
||||
|
||||
This crate contains the centralized logic for Harper's statistics logging.
|
|
@ -2,12 +2,16 @@ mod record;
|
|||
mod summary;
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
pub use record::Record;
|
||||
pub use record::RecordKind;
|
||||
use serde::Serialize;
|
||||
use serde_json::Serializer;
|
||||
pub use summary::Summary;
|
||||
|
||||
/// A collection of logged statistics for the various Harper frontends.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Stats {
|
||||
pub records: Vec<Record>,
|
||||
}
|
||||
|
@ -32,23 +36,31 @@ impl Stats {
|
|||
summary
|
||||
}
|
||||
|
||||
pub fn write_csv(&self, w: &mut impl Write) -> io::Result<()> {
|
||||
let mut writer = csv::WriterBuilder::new().has_headers(false).from_writer(w);
|
||||
|
||||
/// Write the records from `self`.
|
||||
/// Expects the target buffer to either be empty or already be terminated by a newline.
|
||||
pub fn write(&self, w: &mut impl Write) -> io::Result<()> {
|
||||
for record in &self.records {
|
||||
writer.serialize(record)?;
|
||||
let mut serializer = Serializer::new(&mut *w);
|
||||
record.serialize(&mut serializer)?;
|
||||
writeln!(w)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_csv(r: &mut impl Read) -> io::Result<Self> {
|
||||
let mut reader = csv::ReaderBuilder::new().has_headers(false).from_reader(r);
|
||||
/// Read records from a buffer into `self`.
|
||||
/// Assumes the buffer is properly formatted and terminated with a newline.
|
||||
/// An empty buffer will result in no mutation to `self`.
|
||||
pub fn read(r: &mut impl Read) -> io::Result<Self> {
|
||||
let br = BufReader::new(r);
|
||||
let mut records = Vec::new();
|
||||
|
||||
for result in reader.deserialize() {
|
||||
let record: Record = result?;
|
||||
for line_res in br.lines() {
|
||||
let line = line_res?;
|
||||
|
||||
dbg!(&line);
|
||||
|
||||
let record: Record = serde_json::from_str(&line)?;
|
||||
records.push(record);
|
||||
}
|
||||
|
||||
|
@ -61,3 +73,38 @@ impl Default for Stats {
|
|||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use quickcheck::Arbitrary;
|
||||
use quickcheck_macros::quickcheck;
|
||||
|
||||
use crate::{Record, Stats};
|
||||
|
||||
impl Arbitrary for Stats {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let mut stats = Stats::new();
|
||||
|
||||
for _ in 0..g.size() {
|
||||
stats.records.push(Record::arbitrary(g));
|
||||
}
|
||||
|
||||
stats
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn io_is_reversible(ex: Stats) -> bool {
|
||||
let mut written = Vec::new();
|
||||
|
||||
ex.write(&mut written).unwrap();
|
||||
|
||||
let mut readable = Cursor::new(written);
|
||||
|
||||
let read = Stats::read(&mut readable).unwrap();
|
||||
|
||||
ex == read
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use harper_core::linting::LintKind;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
|
||||
pub struct Record {
|
||||
pub kind: RecordKind,
|
||||
/// Recorded as seconds from the Unix Epoch
|
||||
|
@ -23,7 +23,42 @@ impl Record {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum RecordKind {
|
||||
Lint(LintKind),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use harper_core::linting::LintKind;
|
||||
use quickcheck::Arbitrary;
|
||||
|
||||
use super::{Record, RecordKind};
|
||||
|
||||
impl Arbitrary for RecordKind {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
*g.choose(&[
|
||||
Self::Lint(LintKind::Spelling),
|
||||
Self::Lint(LintKind::Capitalization),
|
||||
Self::Lint(LintKind::Style),
|
||||
Self::Lint(LintKind::Formatting),
|
||||
Self::Lint(LintKind::Repetition),
|
||||
Self::Lint(LintKind::Enhancement),
|
||||
Self::Lint(LintKind::Readability),
|
||||
Self::Lint(LintKind::WordChoice),
|
||||
Self::Lint(LintKind::Miscellaneous),
|
||||
])
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Record {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
Record {
|
||||
kind: RecordKind::arbitrary(g),
|
||||
when: u64::arbitrary(g),
|
||||
uuid: uuid::Builder::from_u128(u128::arbitrary(g)).into_uuid(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue