8 Commits

Author SHA1 Message Date
02404792a5 Bump Version 2023-03-19 11:59:39 +01:00
59272ed3e7 Add Complex Output Patterns 2023-03-19 11:57:22 +01:00
322ba65656 Add Caches 2023-03-18 10:27:34 +01:00
b082d6cd8d Fix Imports 2023-03-16 21:33:23 +01:00
f4bcde73f9 Add Roadmap 2023-03-16 21:09:41 +01:00
0e31714582 Add Roadmap 2023-03-16 21:08:56 +01:00
229c858d9a Optimize Workflows 2023-03-16 20:40:37 +01:00
abf6953172 Optimize Workflows 2023-03-16 20:40:17 +01:00
17 changed files with 222 additions and 83 deletions

View File

@ -16,18 +16,37 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Cache Cargo modules
id: cache-cargo
uses: actions/cache@v3
env:
cache-name: cache-cargo-target-debug
with:
path: target
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Build - name: Build
run: cargo build --verbose run: cargo build --verbose
- name: Run tests - name: Run tests
run: cargo test --verbose run: cargo test --verbose -p schemsearch-lib
build-realease:
build-release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Cache Cargo modules
id: cache-cargo
uses: actions/cache@v3
env:
cache-name: cache-cargo-target-release
with:
path: target
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Build - name: Build
run: cargo build --verbose --release run: cargo build --verbose --release -p schemsearch-cli
- name: Upload a Build Artifact - name: Upload a Build Artifact
uses: actions/upload-artifact@v3.1.2 uses: actions/upload-artifact@v3.1.2
with: with:

View File

@ -19,8 +19,18 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Cache Cargo modules
id: cache-cargo
uses: actions/cache@v3
env:
cache-name: cache-cargo-target-release
with:
path: target
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Build - name: Build
run: cargo build --verbose --release run: cargo build --verbose --release -p schemsearch-cli
- name: Create Tarball - name: Create Tarball
if: ${{ matrix.os != 'windows-latest' }} if: ${{ matrix.os != 'windows-latest' }}
run: tar -czvf schemsearch-cli-${{ matrix.os }}.tar.gz -C target/release schemsearch-cli run: tar -czvf schemsearch-cli-${{ matrix.os }}.tar.gz -C target/release schemsearch-cli

View File

@ -2,7 +2,7 @@
members = [ members = [
"schemsearch-cli", "schemsearch-cli",
"schemsearch-lib", "schemsearch-lib",
"schemsearch_faster", "schemsearch-faster",
"schemsearch-files", "schemsearch-files",
"schemsearch-sql", "schemsearch-sql",
"schemsearch-java" "schemsearch-java"

View File

@ -40,6 +40,16 @@ schemsearch-cli --help
--- ---
## Roadmap
- [ ] Tile entities search
- [ ] Entities search
- [ ] Better error handling
- [ ] Web interface
- [ ] McEdit search
---
## Building ## Building
This project is build using Rust for the CLI and library. It can be built using Cargo. This project is build using Rust for the CLI and library. It can be built using Cargo.
```bash ```bash

View File

@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-cli" name = "schemsearch-cli"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
@ -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"]

View File

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

View File

@ -16,14 +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 schemsearch_files::Schematic;
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};
@ -37,7 +37,9 @@ use schemsearch_sql::filter::SchematicFilter;
use schemsearch_sql::load_all_schematics; use schemsearch_sql::load_all_schematics;
#[cfg(feature = "sql")] #[cfg(feature = "sql")]
use crate::types::SqlSchematicSupplier; use crate::types::SqlSchematicSupplier;
use indicatif::{ProgressBar, ParallelProgressIterator, ProgressStyle}; use indicatif::{ParallelProgressIterator, ProgressStyle};
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)>,
} }

View File

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

View File

@ -18,7 +18,6 @@
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(feature = "sql")] #[cfg(feature = "sql")]
use futures::executor::block_on; use futures::executor::block_on;
use schemsearch_files::Schematic;
#[cfg(feature = "sql")] #[cfg(feature = "sql")]
use schemsearch_sql::{load_schemdata, SchematicNode}; use schemsearch_sql::{load_schemdata, SchematicNode};

View File

@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch_faster" name = "schemsearch_faster"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-files" name = "schemsearch-files"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-java" name = "schemsearch-java"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-lib" name = "schemsearch-lib"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"

View File

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

View File

@ -1,6 +1,6 @@
[package] [package]
name = "schemsearch-sql" name = "schemsearch-sql"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"

View File

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