1
0
Mirror von https://github.com/Chaoscaot/schemsearch synchronisiert 2024-11-19 10:20:08 +01:00

Add Complex Output Patterns

Dieser Commit ist enthalten in:
Chaoscaot 2023-03-19 11:57:22 +01:00
Ursprung 322ba65656
Commit 59272ed3e7
6 geänderte Dateien mit 169 neuen und 68 gelöschten Zeilen

Datei anzeigen

@ -16,6 +16,8 @@ futures = { version = "0.3", optional = true }
sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" , "mysql" ], optional = true } sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" , "mysql" ], optional = true }
rayon = "1.7.0" rayon = "1.7.0"
indicatif = { version = "0.17.3", features = ["rayon"] } indicatif = { version = "0.17.3", features = ["rayon"] }
serde = "1.0.157"
serde_json = "1.0.94"
[features] [features]
sql = ["dep:schemsearch-sql", "dep:futures", "dep:sqlx"] sql = ["dep:schemsearch-sql", "dep:futures", "dep:sqlx"]

Datei anzeigen

@ -0,0 +1,31 @@
use serde::{Deserialize, Serialize};
use schemsearch_lib::SearchBehavior;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "event")]
pub enum JsonEvent {
Found(FoundEvent),
Init(InitEvent),
End(EndEvent),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct FoundEvent {
pub name: String,
pub x: u16,
pub y: u16,
pub z: u16,
pub percent: f32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct InitEvent {
pub total: u32,
pub search_behavior: SearchBehavior,
pub start_time: u128,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EndEvent {
pub end_time: u128,
}

Datei anzeigen

@ -16,13 +16,14 @@
*/ */
mod types; mod types;
mod json_output;
mod sinks;
use std::fmt::Debug; use std::fmt::Debug;
use std::fs::File; use std::io::Write;
use std::io;
use std::io::{BufWriter, Write};
use clap::{command, Arg, ArgAction, ValueHint}; use clap::{command, Arg, ArgAction, ValueHint};
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr;
use clap::error::ErrorKind; use clap::error::ErrorKind;
use schemsearch_lib::{search, SearchBehavior}; use schemsearch_lib::{search, SearchBehavior};
use crate::types::{PathSchematicSupplier, SchematicSupplierType}; use crate::types::{PathSchematicSupplier, SchematicSupplierType};
@ -38,6 +39,7 @@ use schemsearch_sql::load_all_schematics;
use crate::types::SqlSchematicSupplier; use crate::types::SqlSchematicSupplier;
use indicatif::{ParallelProgressIterator, ProgressStyle}; use indicatif::{ParallelProgressIterator, ProgressStyle};
use schemsearch_files::Schematic; use schemsearch_files::Schematic;
use crate::sinks::{OutputFormat, OutputSink};
fn main() { fn main() {
#[allow(unused_mut)] #[allow(unused_mut)]
@ -92,20 +94,32 @@ fn main() {
) )
.arg( .arg(
Arg::new("output") Arg::new("output")
.help("The output format") .help("The output format and path [Format:Path] available formats: text, json, csv; available paths: std, (file path)")
.short('o') .short('o')
.long("output") .long("output")
.action(ArgAction::Append) .action(ArgAction::Append)
.default_value("std") .default_value("text:std")
.value_parser(["std_csv", "file_csv", "std", "file"]), .value_parser(|s: &str| {
) let mut split = s.splitn(2, ':');
.arg( let format = match split.next() {
Arg::new("output-file") None => return Err("No format specified".to_string()),
.help("The output file") Some(x) => x
.short('O') };
.long("output-file") let path = match split.next() {
.value_hint(ValueHint::FilePath) None => return Err("No path specified".to_string()),
.action(ArgAction::Append) Some(x) => x
};
let format = match OutputFormat::from_str(format) {
Ok(x) => x,
Err(e) => return Err(e.to_string()),
};
let path = match OutputSink::from_str(path) {
Ok(x) => x,
Err(e) => return Err(e.to_string()),
};
Ok((format, path))
}),
) )
.arg( .arg(
Arg::new("threshold") Arg::new("threshold")
@ -223,52 +237,26 @@ fn main() {
cmd.error(ErrorKind::MissingRequiredArgument, "No schematics specified").exit(); cmd.error(ErrorKind::MissingRequiredArgument, "No schematics specified").exit();
} }
let mut output_std = false; let output: Vec<&(OutputFormat, OutputSink)> = matches.get_many::<(OutputFormat, OutputSink)>("output").expect("Error").collect();
let mut output_std_csv = false; let mut output: Vec<(OutputFormat, Box<dyn Write>)> = output.into_iter().map(|x| (x.0.clone(), x.1.output())).collect();
let mut output_file_csv = false;
let mut output_file = false;
for x in matches.get_many::<String>("output").expect("Couldn't get output") { for x in &mut output {
match x.as_str() { write!(x.1, "{}", x.0.start(schematics.len() as u32, &search_behavior, start.elapsed().as_millis())).unwrap();
"std" => output_std = true,
"std_csv" => output_std_csv = true,
"file_csv" => output_file_csv = true,
"file" => output_file = true,
_ => {}
} }
};
let file: Option<File>;
let mut file_out: Option<BufWriter<File>> = None;
if output_file || output_file_csv {
let output_file_path = match matches.get_one::<String>("output-file") {
None => {
cmd.error(ErrorKind::MissingRequiredArgument, "No output file specified").exit();
}
Some(x) => x
};
file = match File::create(output_file_path) {
Ok(x) => Some(x),
Err(e) => {
cmd.error(ErrorKind::Io, format!("Error while creating output file: {}", e.to_string())).exit();
}
};
file_out = Some(BufWriter::new(file.unwrap()));
}
ThreadPoolBuilder::new().num_threads(*matches.get_one::<usize>("threads").expect("Could not get threads")).build_global().unwrap(); ThreadPoolBuilder::new().num_threads(*matches.get_one::<usize>("threads").expect("Could not get threads")).build_global().unwrap();
let matches: Vec<Result> = schematics.par_iter().progress_with_style(ProgressStyle::with_template("[{elapsed}, ETA: {eta}] {wide_bar} {pos}/{len} {per_sec}").unwrap()).map(|schem| { let matches: Vec<SearchResult> = schematics.par_iter().progress_with_style(ProgressStyle::with_template("[{elapsed}, ETA: {eta}] {wide_bar} {pos}/{len} {per_sec}").unwrap()).map(|schem| {
match schem { match schem {
SchematicSupplierType::PATH(schem) => { SchematicSupplierType::PATH(schem) => {
let schematic = match load_schem(&schem.path) { let schematic = match load_schem(&schem.path) {
Some(x) => x, Some(x) => x,
None => return Result { None => return SearchResult {
name: schem.get_name(), name: schem.get_name(),
matches: vec![] matches: vec![]
} }
}; };
Result { SearchResult {
name: schem.get_name(), name: schem.get_name(),
matches: search(schematic, &pattern, search_behavior) matches: search(schematic, &pattern, search_behavior)
} }
@ -277,7 +265,7 @@ fn main() {
SchematicSupplierType::SQL(schem) => { SchematicSupplierType::SQL(schem) => {
match schem.get_schematic() { match schem.get_schematic() {
Ok(schematic) => { Ok(schematic) => {
Result { SearchResult {
name: schem.get_name(), name: schem.get_name(),
matches: search(schematic, &pattern, search_behavior) matches: search(schematic, &pattern, search_behavior)
} }
@ -286,7 +274,7 @@ fn main() {
if !output_std && !output_std_csv { if !output_std && !output_std_csv {
println!("Error while loading schematic ({}): {}", schem.get_name(), e.to_string()); println!("Error while loading schematic ({}): {}", schem.get_name(), e.to_string());
} }
Result { SearchResult {
name: schem.get_name(), name: schem.get_name(),
matches: vec![] matches: vec![]
} }
@ -296,30 +284,21 @@ fn main() {
} }
}).collect(); }).collect();
let stdout = io::stdout();
let mut lock = stdout.lock();
for matching in matches { for matching in matches {
let schem_name = matching.name; let schem_name = matching.name;
let matching = matching.matches; let matching = matching.matches;
for x in matching { for x in matching {
if output_std { for out in &mut output {
writeln!(lock, "Found match in '{}' at x: {}, y: {}, z: {}, % = {}", schem_name, x.0, x.1, x.2, x.3).unwrap(); write!(out.1, "{}", out.0.found_match(&schem_name, x)).unwrap();
}
if output_std_csv {
writeln!(lock, "{},{},{},{},{}", schem_name, x.0, x.1, x.2, x.3).unwrap();
}
if output_file {
writeln!(file_out.as_mut().unwrap(), "Found match in '{}' at x: {}, y: {}, z: {}, % = {}", schem_name, x.0, x.1, x.2, x.3).unwrap();
}
if output_file_csv {
writeln!(file_out.as_mut().unwrap(), "{},{},{},{},{}", schem_name, x.0, x.1, x.2, x.3).unwrap();
} }
} }
} }
let end = std::time::Instant::now(); let end = std::time::Instant::now();
println!("Finished in {:.2}s! Searched in {} Schematics", end.duration_since(start).as_secs_f32(), schematics.len()); for x in &mut output {
write!(x.1, "{}", x.0.end(end.duration_since(start).as_millis())).unwrap();
x.1.flush().unwrap();
}
} }
fn load_schem(schem_path: &PathBuf) -> Option<Schematic> { fn load_schem(schem_path: &PathBuf) -> Option<Schematic> {
@ -333,7 +312,7 @@ fn load_schem(schem_path: &PathBuf) -> Option<Schematic> {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Result { struct SearchResult {
name: String, name: String,
matches: Vec<(u16, u16, u16, f32)>, matches: Vec<(u16, u16, u16, f32)>,
} }

88
schemsearch-cli/src/sinks.rs Normale Datei
Datei anzeigen

@ -0,0 +1,88 @@
use std::fs::File;
use std::io::BufWriter;
use std::str::FromStr;
use std::io::Write;
use schemsearch_lib::SearchBehavior;
use crate::json_output::{EndEvent, FoundEvent, InitEvent, JsonEvent};
#[derive(Debug, Clone)]
pub enum OutputSink {
Stdout,
File(String),
}
#[derive(Debug, Clone)]
pub enum OutputFormat {
Text,
CSV,
JSON
}
impl FromStr for OutputFormat {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"text" => Ok(OutputFormat::Text),
"csv" => Ok(OutputFormat::CSV),
"json" => Ok(OutputFormat::JSON),
_ => Err(format!("'{}' is not a valid output format", s))
}
}
}
impl FromStr for OutputSink {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"std" => Ok(OutputSink::Stdout),
_ => Ok(OutputSink::File(s.to_string()))
}
}
}
impl OutputSink {
pub fn output(&self) -> Box<dyn Write> {
match self {
OutputSink::Stdout => Box::new(std::io::stdout().lock()),
OutputSink::File(path) => Box::new(BufWriter::new(File::create(path).unwrap()))
}
}
}
impl OutputFormat {
pub fn found_match(&self, name: &String, pos: (u16, u16, u16, f32)) -> String {
match self {
OutputFormat::Text => format!("Found match in '{}' at x: {}, y: {}, z: {}, % = {}\n", name, pos.0, pos.1, pos.2, pos.3),
OutputFormat::CSV => format!("{},{},{},{},{}\n", name, pos.0, pos.1, pos.2, pos.3),
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::Found(FoundEvent {
name: name.clone(),
x: pos.0,
y: pos.1,
z: pos.2,
percent: pos.3,
})).unwrap())
}
}
pub fn start(&self, total: u32, search_behavior: &SearchBehavior, start_time: u128) -> String {
match self {
OutputFormat::Text => format!("Starting search in {} schematics\n", total),
OutputFormat::CSV => format!("Name,X,Y,Z,Percent\n"),
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::Init(InitEvent {
total,
search_behavior: search_behavior.clone(),
start_time,
})).unwrap())
}
}
pub fn end(&self, end_time: u128) -> String {
match self {
OutputFormat::Text => format!("Search complete in {}s\n", end_time / 1000),
OutputFormat::CSV => format!("{}\n", end_time),
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::End(EndEvent{ end_time })).unwrap())
}
}
}

Datei anzeigen

@ -17,11 +17,12 @@
pub mod pattern_mapper; pub mod pattern_mapper;
use serde::{Deserialize, Serialize};
use pattern_mapper::match_palette; use pattern_mapper::match_palette;
use schemsearch_files::Schematic; use schemsearch_files::Schematic;
use crate::pattern_mapper::match_palette_adapt; use crate::pattern_mapper::match_palette_adapt;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct SearchBehavior { pub struct SearchBehavior {
pub ignore_block_data: bool, pub ignore_block_data: bool,
pub ignore_block_entities: bool, pub ignore_block_entities: bool,

Datei anzeigen

@ -16,7 +16,7 @@
*/ */
use std::sync::Mutex; use std::sync::Mutex;
use sqlx::{ConnectOptions, Executor, MySql, MySqlPool, Pool, Row}; use sqlx::{Executor, MySql, Pool, Row};
use sqlx::mysql::{MySqlConnectOptions, MySqlPoolOptions}; use sqlx::mysql::{MySqlConnectOptions, MySqlPoolOptions};
use crate::filter::SchematicFilter; use crate::filter::SchematicFilter;