mirror of
https://github.com/JockeTF/fimfareader.git
synced 2024-11-23 13:58:00 +01:00
Add query module for searching
This commit is contained in:
parent
aa619e862d
commit
36f182620b
6 changed files with 419 additions and 22 deletions
129
Cargo.lock
generated
129
Cargo.lock
generated
|
@ -10,11 +10,6 @@ name = "autocfg"
|
|||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.9"
|
||||
|
@ -28,7 +23,7 @@ dependencies = [
|
|||
"libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -46,7 +41,8 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zip 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -61,6 +57,18 @@ name = "itoa"
|
|||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.60"
|
||||
|
@ -68,16 +76,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lexical-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.41"
|
||||
|
@ -126,27 +148,48 @@ name = "rle-decode-fast"
|
|||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.97"
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.97"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -156,12 +199,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stackvector"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.40"
|
||||
version = "0.15.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -189,6 +246,24 @@ name = "unicode-xid"
|
|||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unreachable"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.7"
|
||||
|
@ -214,21 +289,23 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libflate 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libflate 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
|
||||
"checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b"
|
||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe"
|
||||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum lexical-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b0f90c979adde96d19eb10eb6431ba0c441e2f9e9bdff868b2f6f5114ff519"
|
||||
"checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb"
|
||||
"checksum libflate 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "90c6f86f4b0caa347206f916f8b687b51d77c6ef8ff18d52dd007491fd580529"
|
||||
"checksum libflate 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "45c97cf62125b79dcac52d506acdc4799f21a198597806947fd5f40dc7b93412"
|
||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||
"checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b"
|
||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
|
||||
|
@ -236,14 +313,22 @@ dependencies = [
|
|||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
|
||||
"checksum serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "d46b3dfedb19360a74316866cef04687cd4d6a70df8e6a506c63512790769b72"
|
||||
"checksum serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "c22a0820adfe2f257b098714323563dd06426502abbbce4f51b72ef544c5027f"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113"
|
||||
"checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c"
|
||||
"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704"
|
||||
"checksum syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)" = "bc945221ccf4a7e8c31222b9d1fc77aefdd6638eb901a6ce457a3dc29d4c31e8"
|
||||
"checksum stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4725650978235083241fab0fdc8e694c3de37821524e7534a1a9061d1068af"
|
||||
"checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241"
|
||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
|
|
@ -14,6 +14,9 @@ features = ["serde"]
|
|||
[dependencies.hex]
|
||||
version = "*"
|
||||
|
||||
[dependencies.nom]
|
||||
version = "5"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "*"
|
||||
features = ["derive"]
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
pub mod archive;
|
||||
pub mod error;
|
||||
pub mod query;
|
||||
|
||||
use std::env::args;
|
||||
use std::time::Instant;
|
||||
|
|
6
src/query/mod.rs
Normal file
6
src/query/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
//! Query module.
|
||||
|
||||
mod optimizer;
|
||||
mod parser;
|
||||
|
||||
pub use self::parser::parse;
|
87
src/query/optimizer.rs
Normal file
87
src/query/optimizer.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
//! Query optimizer.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::prelude::*;
|
||||
|
||||
use super::parser::{Operator, Source};
|
||||
use crate::archive::Story;
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
use Operator::*;
|
||||
use Source::*;
|
||||
|
||||
type Filter = Box<dyn Fn(&Story) -> bool + Sync>;
|
||||
|
||||
type IntFn = Box<dyn Fn(&Story) -> i64 + Sync>;
|
||||
type StrFn = Box<dyn Fn(&Story) -> &str + Sync>;
|
||||
type DtuFn = Box<dyn Fn(&Story) -> &Option<DateTime<Utc>> + Sync>;
|
||||
|
||||
macro_rules! ok {
|
||||
($func:expr) => {
|
||||
Ok(Box::new($func))
|
||||
};
|
||||
}
|
||||
|
||||
pub fn optimize(src: Source, op: Operator, value: &str) -> Result<Filter> {
|
||||
match src {
|
||||
StrFn(f) => strfn(f, op, value),
|
||||
IntFn(f) => intfn(f, op, value),
|
||||
DtuFn(f) => dtufn(f, op, value),
|
||||
}
|
||||
}
|
||||
|
||||
fn strfn(f: StrFn, op: Operator, value: &str) -> Result<Filter> {
|
||||
let value: String = match op {
|
||||
Fuzzy => value.to_lowercase(),
|
||||
_ => value.to_owned(),
|
||||
};
|
||||
|
||||
match op {
|
||||
Exact => ok!(move |s| f(s) == &value),
|
||||
Fuzzy => ok!(move |s| f(s).to_lowercase().contains(&value)),
|
||||
_ => Err(Error::query("Invalid operation for text type")),
|
||||
}
|
||||
}
|
||||
|
||||
fn intfn(f: IntFn, op: Operator, value: &str) -> Result<Filter> {
|
||||
let value: i64 = value.parse().map_err(|e| match e {
|
||||
_ => Error::query("Invalid value for number type"),
|
||||
})?;
|
||||
|
||||
match op {
|
||||
Exact => ok!(move |s| f(s) == value),
|
||||
Fuzzy => ok!(move |s| f(s) == value),
|
||||
LessThan => ok!(move |s| f(s) < value),
|
||||
MoreThan => ok!(move |s| f(s) > value),
|
||||
}
|
||||
}
|
||||
|
||||
fn dtufn(f: DtuFn, op: Operator, value: &str) -> Result<Filter> {
|
||||
let parsed = DateTime::from_str(value);
|
||||
|
||||
let value: DateTime<Utc> = parsed.map_err(|e| match e {
|
||||
_ => Error::query("Invalid value for date type"),
|
||||
})?;
|
||||
|
||||
let date = value.date();
|
||||
|
||||
match op {
|
||||
Exact => ok!(move |s| match f(s) {
|
||||
Some(dt) => dt == &value,
|
||||
None => false,
|
||||
}),
|
||||
Fuzzy => ok!(move |s| match f(s) {
|
||||
Some(dt) => dt.date() == date,
|
||||
None => false,
|
||||
}),
|
||||
LessThan => ok!(move |s| match f(s) {
|
||||
Some(dt) => dt < &value,
|
||||
None => false,
|
||||
}),
|
||||
MoreThan => ok!(move |s| match f(s) {
|
||||
Some(dt) => dt > &value,
|
||||
None => false,
|
||||
}),
|
||||
}
|
||||
}
|
215
src/query/parser.rs
Normal file
215
src/query/parser.rs
Normal file
|
@ -0,0 +1,215 @@
|
|||
//! Query parser.
|
||||
|
||||
use chrono::prelude::*;
|
||||
|
||||
use nom::character::complete::*;
|
||||
use nom::error::ErrorKind as NomErrorKind;
|
||||
use nom::sequence::*;
|
||||
use nom::*;
|
||||
|
||||
use super::optimizer::optimize;
|
||||
use crate::archive::Story;
|
||||
use crate::error::*;
|
||||
|
||||
type Filter = Box<dyn Fn(&Story) -> bool + Sync>;
|
||||
|
||||
pub enum Source {
|
||||
IntFn(Box<dyn Fn(&Story) -> i64 + Sync>),
|
||||
StrFn(Box<dyn Fn(&Story) -> &str + Sync>),
|
||||
DtuFn(Box<dyn Fn(&Story) -> &Option<DateTime<Utc>> + Sync>),
|
||||
}
|
||||
|
||||
pub enum Operator {
|
||||
Exact,
|
||||
Fuzzy,
|
||||
LessThan,
|
||||
MoreThan,
|
||||
}
|
||||
|
||||
macro_rules! sfn {
|
||||
($func:expr) => {
|
||||
|_| Source::StrFn(Box::new($func))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! ifn {
|
||||
($func:expr) => {
|
||||
|_| Source::IntFn(Box::new($func))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! dfn {
|
||||
($func:expr) => {
|
||||
|_| Source::DtuFn(Box::new($func))
|
||||
};
|
||||
}
|
||||
|
||||
named!(source<&str, Source>, preceded!(space0, alt!(
|
||||
tag!("id") => { ifn!(|s| s.id as i64) } |
|
||||
|
||||
tag!("story") => { sfn!(|s| &s.title) } |
|
||||
tag!("title") => { sfn!(|s| &s.title) } |
|
||||
|
||||
tag!("description") => { sfn!(|s| &s.description_html) } |
|
||||
tag!("short description") => { sfn!(|s| &s.short_description) } |
|
||||
tag!("url") => { sfn!(|s| &s.url) } |
|
||||
|
||||
tag!("modified") => { dfn!(|s| &s.date_modified) } |
|
||||
tag!("published") => { dfn!(|s| &s.date_published) } |
|
||||
tag!("updated") => { dfn!(|s| &s.date_updated) } |
|
||||
|
||||
tag!("chapters") => { ifn!(|s| s.num_chapters as i64) } |
|
||||
tag!("comments") => { ifn!(|s| s.num_comments as i64) } |
|
||||
tag!("dislikes") => { ifn!(|s| s.num_dislikes as i64) } |
|
||||
tag!("likes") => { ifn!(|s| s.num_likes as i64) } |
|
||||
tag!("total views") => { ifn!(|s| s.total_num_views as i64) } |
|
||||
tag!("views") => { ifn!(|s| s.num_views as i64) } |
|
||||
tag!("words") => { ifn!(|s| s.num_words as i64) } |
|
||||
|
||||
tag!("author") => { sfn!(|s| &s.author.name) } |
|
||||
tag!("author name") => { sfn!(|s| &s.author.name) } |
|
||||
|
||||
tag!("author id") => { ifn!(|s| s.author.id as i64) } |
|
||||
tag!("author joined") => { dfn!(|s| &s.author.date_joined) } |
|
||||
|
||||
tag!("path") => { sfn!(|s| &s.archive.path) } |
|
||||
tag!("archive") => { sfn!(|s| &s.archive.path) } |
|
||||
tag!("archive path") => { sfn!(|s| &s.archive.path) } |
|
||||
|
||||
tag!("entry checked") => { dfn!(|s| &s.archive.date_checked) } |
|
||||
tag!("entry created") => { dfn!(|s| &s.archive.date_created) } |
|
||||
tag!("entry fetched") => { dfn!(|s| &s.archive.date_fetched) } |
|
||||
tag!("entry updated") => { dfn!(|s| &s.archive.date_updated) }
|
||||
)));
|
||||
|
||||
named!(operator<&str, Operator>, preceded!(space0, alt!(
|
||||
tag!("=") => { |_| Operator::Exact } |
|
||||
tag!(":") => { |_| Operator::Fuzzy } |
|
||||
tag!("<") => { |_| Operator::LessThan } |
|
||||
tag!(">") => { |_| Operator::MoreThan }
|
||||
)));
|
||||
|
||||
fn unescape(input: &str) -> String {
|
||||
input
|
||||
.replace("\\)", ")")
|
||||
.replace("\\,", ",")
|
||||
.replace("\\|", "|")
|
||||
.replace("\\\\", "\\")
|
||||
}
|
||||
|
||||
named!(value<&str, &str>,
|
||||
escaped!(none_of!("),|\\"), '\\', one_of!("),|\\"))
|
||||
);
|
||||
|
||||
named!(target<&str, String>, preceded!(space0,
|
||||
map!(value, |value| unescape(value.trim()))
|
||||
));
|
||||
|
||||
fn item(input: &str) -> IResult<&str, Filter> {
|
||||
let result = tuple((source, operator, target))(input)?;
|
||||
let (left, (src, op, value)) = result;
|
||||
|
||||
let filter = optimize(src, op, &value).map_err(|e| match e {
|
||||
_ => Err::Failure((input, NomErrorKind::Permutation)),
|
||||
})?;
|
||||
|
||||
Ok((left, filter))
|
||||
}
|
||||
|
||||
named!(parens<&str, Filter>, alt!(
|
||||
delimited!(
|
||||
preceded!(space0, tag!("(")),
|
||||
preceded!(space0, call!(ofunc)),
|
||||
preceded!(space0, tag!(")"))
|
||||
) |
|
||||
call!(item)
|
||||
));
|
||||
|
||||
fn negate(input: &str) -> IResult<&str, Filter> {
|
||||
let (left, filter) = parens(input)?;
|
||||
|
||||
Ok((left, Box::new(move |s| !filter(s))))
|
||||
}
|
||||
|
||||
named!(nlist<&str, Filter>, preceded!(space0, alt!(
|
||||
preceded!(char('!'), call!(negate)) | call!(parens)
|
||||
)));
|
||||
|
||||
named!(alist<&str, Vec<Filter>>, separated_nonempty_list!(
|
||||
preceded!(space0, char(',')), call!(nlist)
|
||||
));
|
||||
|
||||
fn afunc(input: &str) -> IResult<&str, Filter> {
|
||||
let (left, mut filters) = alist(input)?;
|
||||
|
||||
if filters.len() == 1 {
|
||||
return Ok((left, filters.remove(0)));
|
||||
}
|
||||
|
||||
let filter: Filter = Box::new(move |story| {
|
||||
for filter in filters.iter() {
|
||||
if !filter(story) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
Ok((left, filter))
|
||||
}
|
||||
|
||||
named!(olist<&str, Vec<Filter>>, separated_nonempty_list!(
|
||||
preceded!(space0, char('|')), call!(afunc)
|
||||
));
|
||||
|
||||
fn ofunc(input: &str) -> IResult<&str, Filter> {
|
||||
let (left, mut filters) = olist(input)?;
|
||||
|
||||
if filters.len() == 1 {
|
||||
return Ok((left, filters.remove(0)));
|
||||
}
|
||||
|
||||
let filter: Filter = Box::new(move |story| {
|
||||
for filter in filters.iter() {
|
||||
if filter(story) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
Ok((left, filter))
|
||||
}
|
||||
|
||||
fn format_error(query: &str, input: &str, error: NomErrorKind) -> String {
|
||||
let description = error.description().to_lowercase();
|
||||
let position = query.len() - input.len();
|
||||
|
||||
format!("Invalid {} at {}", description, position)
|
||||
}
|
||||
|
||||
fn translate_error(query: &str, error: Err<(&str, NomErrorKind)>) -> Error {
|
||||
let message = match error {
|
||||
Err::Error((i, e)) => format_error(query, i, e),
|
||||
Err::Failure((i, e)) => format_error(query, i, e),
|
||||
Err::Incomplete(_) => String::from("Incomplete input"),
|
||||
};
|
||||
|
||||
Error::query(message)
|
||||
}
|
||||
|
||||
fn translate_incomplete(query: &str, input: &str) -> Error {
|
||||
let position = query.len() - input.len();
|
||||
|
||||
Error::query(format!("Incomplete input at {}", position))
|
||||
}
|
||||
|
||||
pub fn parse(query: &str) -> Result<Filter> {
|
||||
match ofunc(query.trim()) {
|
||||
Ok(("", filter)) => Ok(filter),
|
||||
Ok((i, _)) => Err(translate_incomplete(query, i)),
|
||||
Err(e) => Err(translate_error(query, e)),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue