mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 10:04:12 +02:00
support selecting a file from an archive
This commit is contained in:
parent
4f3d6a17ab
commit
9634736d8c
5 changed files with 94 additions and 22 deletions
64
Cargo.lock
generated
64
Cargo.lock
generated
|
|
@ -231,6 +231,19 @@ dependencies = [
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"unicode-width 0.1.14",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
@ -386,6 +399,19 @@ dependencies = [
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dialoguer"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"shell-words",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
|
@ -414,6 +440,12 @@ version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -430,6 +462,12 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.25"
|
version = "0.2.25"
|
||||||
|
|
@ -607,6 +645,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.161"
|
version = "0.2.161"
|
||||||
|
|
@ -661,6 +705,7 @@ dependencies = [
|
||||||
"bzip2-rs",
|
"bzip2-rs",
|
||||||
"clap",
|
"clap",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
|
"dialoguer",
|
||||||
"flate2",
|
"flate2",
|
||||||
"hdrhistogram",
|
"hdrhistogram",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
|
@ -1091,6 +1136,12 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shell-words"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
@ -1212,6 +1263,19 @@ dependencies = [
|
||||||
"xattr",
|
"xattr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ flate2 = "1.0.31"
|
||||||
xz2 = "0.1.7"
|
xz2 = "0.1.7"
|
||||||
bzip2-rs = "0.1.2"
|
bzip2-rs = "0.1.2"
|
||||||
ruzstd = "0.7.2"
|
ruzstd = "0.7.2"
|
||||||
|
dialoguer = "0.11.0"
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||||
tikv-jemallocator = "0.6.0"
|
tikv-jemallocator = "0.6.0"
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ pub enum ReadError {
|
||||||
Zip(#[from] ZipError),
|
Zip(#[from] ZipError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Zstd(#[from] FrameDecoderError),
|
Zstd(#[from] FrameDecoderError),
|
||||||
#[error("archive contains multiple files")]
|
|
||||||
MultipleFiles,
|
|
||||||
#[error("archive contains no files")]
|
#[error("archive contains no files")]
|
||||||
NoFiles,
|
NoFiles,
|
||||||
#[error("log file contained non-utf8 characters: {0:#}")]
|
#[error("log file contained non-utf8 characters: {0:#}")]
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ mod archive;
|
||||||
use crate::error::ReadError;
|
use crate::error::ReadError;
|
||||||
use crate::logfile::archive::{Archive, ArchiveEntry, TarArchive, ZipArchive};
|
use crate::logfile::archive::{Archive, ArchiveEntry, TarArchive, ZipArchive};
|
||||||
use bzip2_rs::DecoderReader;
|
use bzip2_rs::DecoderReader;
|
||||||
|
use dialoguer::Select;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use ruzstd::StreamingDecoder;
|
use ruzstd::StreamingDecoder;
|
||||||
use std::fs::File;
|
use std::io::{Cursor, Read, Seek};
|
||||||
use std::io::{BufReader, Read};
|
|
||||||
use xz2::read::XzDecoder;
|
use xz2::read::XzDecoder;
|
||||||
|
|
||||||
pub struct LogFile {
|
pub struct LogFile {
|
||||||
|
|
@ -14,14 +14,10 @@ pub struct LogFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogFile {
|
impl LogFile {
|
||||||
pub fn open(path: &str) -> Result<LogFile, ReadError> {
|
pub fn open<R: Read + Seek>(path: &str, file: R) -> Result<LogFile, ReadError> {
|
||||||
let file = File::open(path)?;
|
|
||||||
let file = BufReader::new(file);
|
|
||||||
if path.ends_with(".zip") {
|
if path.ends_with(".zip") {
|
||||||
let mut zip = ZipArchive::new(file)?;
|
let mut zip = ZipArchive::new(file)?;
|
||||||
let content = select_file(&mut zip)?;
|
return select_file(&mut zip);
|
||||||
|
|
||||||
return Ok(LogFile { content });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = path.strip_suffix(".gz") {
|
if let Some(path) = path.strip_suffix(".gz") {
|
||||||
|
|
@ -44,9 +40,7 @@ impl LogFile {
|
||||||
fn open_no_seek<R: Read>(path: &str, mut file: R) -> Result<LogFile, ReadError> {
|
fn open_no_seek<R: Read>(path: &str, mut file: R) -> Result<LogFile, ReadError> {
|
||||||
if path.ends_with(".tar") {
|
if path.ends_with(".tar") {
|
||||||
let mut zip = TarArchive::new(file)?;
|
let mut zip = TarArchive::new(file)?;
|
||||||
let content = select_file(&mut zip)?;
|
select_file(&mut zip)
|
||||||
|
|
||||||
Ok(LogFile { content })
|
|
||||||
} else {
|
} else {
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
file.read_to_string(&mut content)?;
|
file.read_to_string(&mut content)?;
|
||||||
|
|
@ -64,21 +58,32 @@ impl LogFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_file<A: Archive>(archive: &mut A) -> Result<String, ReadError> {
|
fn select_file<A: Archive>(archive: &mut A) -> Result<LogFile, ReadError> {
|
||||||
let entry = {
|
let entry = {
|
||||||
let mut entries = archive
|
let mut entries = archive
|
||||||
.entries()
|
.entries()
|
||||||
.filter(|entry| !entry.name().starts_with("__MACOSX"))
|
.filter(|entry| !entry.name().starts_with("__MACOSX") && !entry.name().ends_with('/'))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// todo: present a picker instead
|
if entries.is_empty() {
|
||||||
if entries.len() > 1 {
|
|
||||||
return Err(ReadError::MultipleFiles);
|
|
||||||
} else if entries.is_empty() {
|
|
||||||
return Err(ReadError::NoFiles);
|
return Err(ReadError::NoFiles);
|
||||||
}
|
}
|
||||||
entries.pop().unwrap()
|
|
||||||
|
let index = if entries.len() == 1 {
|
||||||
|
0usize
|
||||||
|
} else {
|
||||||
|
let names = entries.iter().map(A::Entry::name).collect::<Vec<_>>();
|
||||||
|
Select::new()
|
||||||
|
.with_prompt("Select file to load?")
|
||||||
|
.items(&names)
|
||||||
|
.interact()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
entries.remove(index)
|
||||||
};
|
};
|
||||||
|
let name = entry.name().to_string();
|
||||||
let raw = entry.extract()?;
|
let raw = entry.extract()?;
|
||||||
Ok(String::from_utf8(raw)?)
|
|
||||||
|
LogFile::open(&name, Cursor::new(raw))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ use main_error::MainResult;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
|
|
@ -39,7 +41,9 @@ struct Args {
|
||||||
fn main() -> MainResult {
|
fn main() -> MainResult {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let log_file = LogFile::open(&args.file).map_err(|err| LogError::Read {
|
let file = File::open(&args.file)?;
|
||||||
|
let file = BufReader::new(file);
|
||||||
|
let log_file = LogFile::open(&args.file, file).map_err(|err| LogError::Read {
|
||||||
err,
|
err,
|
||||||
path: args.file,
|
path: args.file,
|
||||||
})?;
|
})?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue