Initial path matching
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
insert_final_newline = false
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.DS_Store
|
297
Cargo.lock
generated
Normal file
297
Cargo.lock
generated
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pick"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_write",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_write"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
65
Cargo.toml
Normal file
65
Cargo.toml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
[package]
|
||||||
|
name = "pick"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
#
|
||||||
|
# MARK: lints
|
||||||
|
#
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unused_import_braces = "deny"
|
||||||
|
unit_bindings = "deny"
|
||||||
|
single_use_lifetimes = "deny"
|
||||||
|
non_ascii_idents = "deny"
|
||||||
|
macro_use_extern_crate = "deny"
|
||||||
|
elided_lifetimes_in_paths = "deny"
|
||||||
|
absolute_paths_not_starting_with_crate = "deny"
|
||||||
|
explicit_outlives_requirements = "warn"
|
||||||
|
unused_crate_dependencies = "warn"
|
||||||
|
redundant_lifetimes = "warn"
|
||||||
|
missing_docs = "allow"
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
needless_return = "allow"
|
||||||
|
new_without_default = "allow"
|
||||||
|
tabs_in_doc_comments = "allow"
|
||||||
|
dbg_macro = "deny"
|
||||||
|
allow_attributes = "deny"
|
||||||
|
create_dir = "deny"
|
||||||
|
filetype_is_file = "deny"
|
||||||
|
integer_division = "deny"
|
||||||
|
lossy_float_literal = "deny"
|
||||||
|
map_err_ignore = "deny"
|
||||||
|
mutex_atomic = "deny"
|
||||||
|
needless_raw_strings = "deny"
|
||||||
|
print_stderr = "warn"
|
||||||
|
print_stdout = "warn"
|
||||||
|
str_to_string = "deny"
|
||||||
|
string_add = "deny"
|
||||||
|
string_to_string = "deny"
|
||||||
|
unimplemented = "deny"
|
||||||
|
use_debug = "allow"
|
||||||
|
verbose_file_reads = "deny"
|
||||||
|
large_types_passed_by_value = "deny"
|
||||||
|
large_enum_variant = "allow"
|
||||||
|
match_on_vec_items = "deny"
|
||||||
|
wildcard_dependencies = "deny"
|
||||||
|
negative_feature_names = "deny"
|
||||||
|
redundant_feature_names = "deny"
|
||||||
|
multiple_crate_versions = "allow"
|
||||||
|
missing_safety_doc = "warn"
|
||||||
|
identity_op = "allow"
|
||||||
|
comparison_chain = "allow"
|
||||||
|
unwrap_used = "deny"
|
||||||
|
|
||||||
|
#
|
||||||
|
# MARK: deps
|
||||||
|
#
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
indexmap = { version = "2.9.0", features = ["serde"] }
|
||||||
|
regex = "1.11.1"
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
toml = { version = "0.8.22", features = ["preserve_order"] }
|
||||||
|
walkdir = "2.5.0"
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
hard_tabs = true
|
33
src/main.rs
Normal file
33
src/main.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
pub mod manifest;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let file = std::fs::read_to_string("./test.toml").unwrap();
|
||||||
|
let x: manifest::Manifest = toml::from_str(&file).unwrap();
|
||||||
|
|
||||||
|
let rules = x
|
||||||
|
.rules
|
||||||
|
.iter()
|
||||||
|
.map(|rule| (rule.regex(), rule.action))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let walker = WalkDir::new("./target").into_iter();
|
||||||
|
|
||||||
|
for entry in walker {
|
||||||
|
let e = entry.unwrap();
|
||||||
|
let p = e.path();
|
||||||
|
let s = p.to_str().unwrap();
|
||||||
|
|
||||||
|
if !p.is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let m = rules
|
||||||
|
.iter()
|
||||||
|
.find(|(r, _)| r.is_match(s))
|
||||||
|
.map(|x| x.1.clone());
|
||||||
|
|
||||||
|
println!(" {m:?} {s}")
|
||||||
|
}
|
||||||
|
}
|
389
src/manifest.rs
Normal file
389
src/manifest.rs
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
use indexmap::IndexMap;
|
||||||
|
use regex::Regex;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Manifest {
|
||||||
|
pub config: PickConfig,
|
||||||
|
pub rules: PickRules,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct PickConfig {
|
||||||
|
pub source: PathBuf,
|
||||||
|
pub target: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: rules
|
||||||
|
//
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum OptVec<T> {
|
||||||
|
Single(T),
|
||||||
|
Vec(Vec<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OptVec<T> {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Single(_) => 1,
|
||||||
|
Self::Vec(v) => v.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, idx: usize) -> Option<&T> {
|
||||||
|
match self {
|
||||||
|
Self::Single(t) => (idx == 0).then_some(t),
|
||||||
|
Self::Vec(v) => v.get(idx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PickRule {
|
||||||
|
Plain(String),
|
||||||
|
Nested(PickRules),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct PickRules(OptVec<IndexMap<String, PickRule>>);
|
||||||
|
|
||||||
|
impl PickRules {
|
||||||
|
pub fn iter<'a>(&'a self) -> PickRuleIterator<'a> {
|
||||||
|
PickRuleIterator {
|
||||||
|
stack: vec![PickRuleIterState {
|
||||||
|
rules: self,
|
||||||
|
map_index: 0,
|
||||||
|
entry_index: 0,
|
||||||
|
prefix: Vec::new(),
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a PickRules {
|
||||||
|
type Item = FlatPickRule;
|
||||||
|
type IntoIter = PickRuleIterator<'a>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: rule iterator
|
||||||
|
//
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FlatPickRule {
|
||||||
|
pub patterns: Vec<String>,
|
||||||
|
pub action: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlatPickRule {
|
||||||
|
pub fn regex(&self) -> Regex {
|
||||||
|
Regex::new(
|
||||||
|
&self
|
||||||
|
.patterns
|
||||||
|
.join("/")
|
||||||
|
.split("/")
|
||||||
|
.map(|x| match x {
|
||||||
|
"**" => "((:?[^/]+)*)".to_owned(),
|
||||||
|
"*" => "([^/]+)".to_owned(),
|
||||||
|
x => regex::escape(x),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("/"),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PickRuleIterState<'a> {
|
||||||
|
rules: &'a PickRules,
|
||||||
|
map_index: usize,
|
||||||
|
entry_index: usize,
|
||||||
|
prefix: Vec<String>,
|
||||||
|
}
|
||||||
|
pub struct PickRuleIterator<'a> {
|
||||||
|
stack: Vec<PickRuleIterState<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for PickRuleIterator<'_> {
|
||||||
|
type Item = FlatPickRule;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.stack.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = self.stack.last_mut().unwrap();
|
||||||
|
|
||||||
|
if current.map_index >= current.rules.0.len() {
|
||||||
|
self.stack.pop();
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_map = ¤t.rules.0.get(current.map_index).unwrap();
|
||||||
|
|
||||||
|
if current.entry_index >= current_map.len() {
|
||||||
|
current.map_index += 1;
|
||||||
|
current.entry_index = 0;
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (key, value) = current_map.get_index(current.entry_index).unwrap();
|
||||||
|
|
||||||
|
current.entry_index += 1;
|
||||||
|
|
||||||
|
match value {
|
||||||
|
PickRule::Plain(action) => {
|
||||||
|
let mut patterns = current.prefix.clone();
|
||||||
|
patterns.push(key.to_string());
|
||||||
|
|
||||||
|
Some(FlatPickRule {
|
||||||
|
patterns,
|
||||||
|
action: action.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
PickRule::Nested(nested_rules) => {
|
||||||
|
let mut prefix = current.prefix.clone();
|
||||||
|
prefix.push(key.to_string());
|
||||||
|
|
||||||
|
self.stack.push(PickRuleIterState {
|
||||||
|
rules: nested_rules,
|
||||||
|
map_index: 0,
|
||||||
|
entry_index: 0,
|
||||||
|
prefix,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: tests
|
||||||
|
//
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_simple_manifest() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"*.rs" = "copy"
|
||||||
|
"*.md" = "ignore"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let manifest: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manifest.config.source, Path::new("./src"));
|
||||||
|
assert_eq!(manifest.config.target, Path::new("./tgt"));
|
||||||
|
|
||||||
|
let rules: Vec<FlatPickRule> = manifest.rules.iter().collect();
|
||||||
|
assert_eq!(rules.len(), 2);
|
||||||
|
|
||||||
|
assert_eq!(rules[0].patterns, vec!["*.rs"]);
|
||||||
|
assert_eq!(rules[0].action, "copy");
|
||||||
|
|
||||||
|
assert_eq!(rules[1].patterns, vec!["*.md"]);
|
||||||
|
assert_eq!(rules[1].action, "ignore");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rule_ordering_preserved() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"third" = "c"
|
||||||
|
"first" = "a"
|
||||||
|
"second" = "b"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let manifest: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
let rules: Vec<FlatPickRule> = manifest.rules.iter().collect();
|
||||||
|
|
||||||
|
assert_eq!(rules.len(), 3);
|
||||||
|
assert_eq!(rules[0].patterns, vec!["third"]);
|
||||||
|
assert_eq!(rules[0].action, "c");
|
||||||
|
assert_eq!(rules[1].patterns, vec!["first"]);
|
||||||
|
assert_eq!(rules[1].action, "a");
|
||||||
|
assert_eq!(rules[2].patterns, vec!["second"]);
|
||||||
|
assert_eq!(rules[2].action, "b");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_rules_order() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"a" = "action_a"
|
||||||
|
"b" = "action_b"
|
||||||
|
|
||||||
|
[[rules."nested"]]
|
||||||
|
"c" = "action_c"
|
||||||
|
"d" = "action_d"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"e" = "action_e"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let manifest: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
let rules: Vec<FlatPickRule> = manifest.rules.iter().collect();
|
||||||
|
|
||||||
|
assert_eq!(rules.len(), 5);
|
||||||
|
assert_eq!(rules[0].patterns, vec!["a"]);
|
||||||
|
assert_eq!(rules[0].action, "action_a");
|
||||||
|
assert_eq!(rules[1].patterns, vec!["b"]);
|
||||||
|
assert_eq!(rules[1].action, "action_b");
|
||||||
|
assert_eq!(rules[2].patterns, vec!["nested", "c"]);
|
||||||
|
assert_eq!(rules[2].action, "action_c");
|
||||||
|
assert_eq!(rules[3].patterns, vec!["nested", "d"]);
|
||||||
|
assert_eq!(rules[3].action, "action_d");
|
||||||
|
assert_eq!(rules[4].patterns, vec!["e"]);
|
||||||
|
assert_eq!(rules[4].action, "action_e");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deeply_nested_rules() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules."a"."b"."c"]]
|
||||||
|
"d" = "action_d"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let manifest: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
let rules: Vec<FlatPickRule> = manifest.rules.iter().collect();
|
||||||
|
|
||||||
|
assert_eq!(rules.len(), 1);
|
||||||
|
assert_eq!(rules[0].patterns, vec!["a", "b", "c", "d"]);
|
||||||
|
assert_eq!(rules[0].action, "action_d");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_maps_same_level() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"a1" = "copy"
|
||||||
|
"a2" = "ignore"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"b1" = "copy"
|
||||||
|
"b2" = "ignore"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let manifest: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
let rules: Vec<FlatPickRule> = manifest.rules.iter().collect();
|
||||||
|
|
||||||
|
// Test that all rules exist and are in the correct order
|
||||||
|
assert_eq!(rules.len(), 4);
|
||||||
|
assert_eq!(rules[0].patterns, vec!["a1"]);
|
||||||
|
assert_eq!(rules[0].action, "copy");
|
||||||
|
assert_eq!(rules[1].patterns, vec!["a2"]);
|
||||||
|
assert_eq!(rules[1].action, "ignore");
|
||||||
|
assert_eq!(rules[2].patterns, vec!["b1"]);
|
||||||
|
assert_eq!(rules[2].action, "copy");
|
||||||
|
assert_eq!(rules[3].patterns, vec!["b2"]);
|
||||||
|
assert_eq!(rules[3].action, "ignore");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_rules_list() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let manifest: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
let rules: Vec<FlatPickRule> = manifest.rules.iter().collect();
|
||||||
|
|
||||||
|
assert_eq!(rules.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "missing field `config`")]
|
||||||
|
fn missing_config() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[[rules]]
|
||||||
|
"a" = "copy"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let _: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "missing field `source`")]
|
||||||
|
fn incomplete_config() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"a" = "copy"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let _: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn invalid_toml_syntax() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"invalid" = { this is not valid TOML }
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let _: Manifest = toml::from_str(toml_str).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mixed_rule_types() {
|
||||||
|
let toml_str = r#"
|
||||||
|
[config]
|
||||||
|
source = "./src"
|
||||||
|
target = "./tgt"
|
||||||
|
|
||||||
|
[[rules]]
|
||||||
|
"plain" = "copy"
|
||||||
|
"nested" = { invalid_as_string = true }
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// This should fail because a table is not a valid PickRule
|
||||||
|
let result = toml::from_str::<Manifest>(toml_str);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user