init
This commit is contained in:
commit
ef0b5011ce
19 changed files with 549 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.direnv/
|
||||||
|
*.pdf
|
||||||
|
**/target
|
81
flake.lock
Normal file
81
flake.lock
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"crane": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729273024,
|
||||||
|
"narHash": "sha256-Mb5SemVsootkn4Q2IiY0rr9vrXdCCpQ9HnZeD/J3uXs=",
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"rev": "fa8b7445ddadc37850ed222718ca86622be01967",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fenix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": []
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729375822,
|
||||||
|
"narHash": "sha256-bRo4xVwUhvJ4Gz+OhWMREFMdBOYSw4Yi1Apj01ebbug=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "2853e7d9b5c52a148a9fb824bfe4f9f433f557ab",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729265718,
|
||||||
|
"narHash": "sha256-4HQI+6LsO3kpWTYuVGIzhJs1cetFcwT7quWCk/6rqeo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ccc0c2126893dd20963580b6478d1a10a4512185",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"crane": "crane",
|
||||||
|
"fenix": "fenix",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
61
flake.nix
Normal file
61
flake.nix
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
description = "jftt";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
systems.url = "github:nix-systems/default";
|
||||||
|
crane.url = "github:ipetkov/crane";
|
||||||
|
fenix = {
|
||||||
|
url = "github:nix-community/fenix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.rust-analyzer-src.follows = "";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, systems, crane, fenix, ... }: let
|
||||||
|
name = "jftt";
|
||||||
|
|
||||||
|
forEachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||||
|
pkgsForEach = nixpkgs.legacyPackages;
|
||||||
|
in {
|
||||||
|
# packages = forEachSystem (
|
||||||
|
# system: let
|
||||||
|
# pkgs = pkgsForEach.${system};
|
||||||
|
# craneLib = (crane.mkLib pkgs).overrideToolchain (
|
||||||
|
# fenix.packages.${system}.complete.withComponents [
|
||||||
|
# "cargo"
|
||||||
|
# "rustc"
|
||||||
|
# "rust-src"
|
||||||
|
# ]
|
||||||
|
# );
|
||||||
|
# in {
|
||||||
|
# default = craneLib.buildPackage {
|
||||||
|
# pname = name;
|
||||||
|
# version = "0.1.0";
|
||||||
|
# src = craneLib.cleanCargoSource ./.;
|
||||||
|
# };
|
||||||
|
# }
|
||||||
|
# );
|
||||||
|
|
||||||
|
devShells = forEachSystem (
|
||||||
|
system: let
|
||||||
|
pkgs = pkgsForEach.${system};
|
||||||
|
in {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
inherit name;
|
||||||
|
|
||||||
|
packages = with pkgs; [
|
||||||
|
rust-analyzer
|
||||||
|
(fenix.packages.${system}.complete.withComponents [
|
||||||
|
"cargo"
|
||||||
|
"rustc"
|
||||||
|
"rust-src"
|
||||||
|
])
|
||||||
|
];
|
||||||
|
|
||||||
|
# inputsFrom = [ self.packages.${system}.default ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
14
l1/fa/Cargo.lock
generated
Normal file
14
l1/fa/Cargo.lock
generated
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fa"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"string-matcher-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string-matcher-lib"
|
||||||
|
version = "0.1.0"
|
7
l1/fa/Cargo.toml
Normal file
7
l1/fa/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "fa"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
string-matcher-lib = { path = "../string-matcher-lib" }
|
74
l1/fa/src/lib.rs
Normal file
74
l1/fa/src/lib.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// CLRS 4th edition 2022
|
||||||
|
//
|
||||||
|
// COMPUTE-TRANSITION-FUNCTION(P, Σ, m)
|
||||||
|
// for q = 0 to m
|
||||||
|
// for each character a ∈ Σ
|
||||||
|
// k = min{m, q + 1}
|
||||||
|
// while P[:k] is not a suffix of P[:q]a
|
||||||
|
// k = k - 1
|
||||||
|
// δ(q, a) = k
|
||||||
|
// return δ
|
||||||
|
//
|
||||||
|
// FINITE-AUTOMATON-MATCHER(T, δ, n, m)
|
||||||
|
// q = 0
|
||||||
|
// for i = 1 to n
|
||||||
|
// q = δ(q, T[i])
|
||||||
|
// if q == m
|
||||||
|
// print "Pattern occurs with shift" i - m
|
||||||
|
//
|
||||||
|
// T - input text
|
||||||
|
// δ - transition function
|
||||||
|
// n - input length
|
||||||
|
// m - pattern length
|
||||||
|
#![feature(pattern)]
|
||||||
|
|
||||||
|
use std::{collections::HashMap, str::pattern::Pattern};
|
||||||
|
use string_matcher_lib::StringMatcherError;
|
||||||
|
|
||||||
|
fn compute_transition_function(pattern: &str, alphabet: &str) -> HashMap<(usize, char), usize> {
|
||||||
|
let m = pattern.len();
|
||||||
|
|
||||||
|
let mut f = HashMap::new();
|
||||||
|
|
||||||
|
for q in 0..=m {
|
||||||
|
for a in alphabet.chars() {
|
||||||
|
let mut k = m.min(q + 1);
|
||||||
|
// I'm sorry I know this is ugly
|
||||||
|
while !pattern[..k].is_suffix_of(format!("{}{a}", pattern[..q].to_string()).as_str()) && k > 0 {
|
||||||
|
k -= 1;
|
||||||
|
}
|
||||||
|
f.insert((q, a), k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string_matcher(text: &str, pattern: &str) -> Result<Vec<usize>, StringMatcherError> {
|
||||||
|
let n = text.len();
|
||||||
|
let m = pattern.len();
|
||||||
|
|
||||||
|
let alphabet = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ĄĆĘŁŃÓŚŻŹąćęłńóśżź";
|
||||||
|
let f = compute_transition_function(pattern, alphabet);
|
||||||
|
|
||||||
|
let mut offsets = vec![];
|
||||||
|
|
||||||
|
let mut q = 0;
|
||||||
|
for i in 0..n {
|
||||||
|
use StringMatcherError::*;
|
||||||
|
let ch = match text.chars().nth(i) {
|
||||||
|
Some(c) => if alphabet.contains(c) { c } else {
|
||||||
|
return Err(InvalidCharacter(i, c));
|
||||||
|
},
|
||||||
|
None => return Err(ReadError(i)),
|
||||||
|
};
|
||||||
|
|
||||||
|
q = *f.get(&(q, ch)).unwrap();
|
||||||
|
|
||||||
|
if q == m {
|
||||||
|
offsets.push(i + 1 - m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(offsets)
|
||||||
|
}
|
14
l1/kmp/Cargo.lock
generated
Normal file
14
l1/kmp/Cargo.lock
generated
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kmp"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"string-matcher-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string-matcher-lib"
|
||||||
|
version = "0.1.0"
|
7
l1/kmp/Cargo.toml
Normal file
7
l1/kmp/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "kmp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
string-matcher-lib = { path = "../string-matcher-lib" }
|
87
l1/kmp/src/lib.rs
Normal file
87
l1/kmp/src/lib.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// CLRS 4th edition 2022
|
||||||
|
//
|
||||||
|
// KMP-MATCHER(T, P, n, m)
|
||||||
|
// π = COMPUTE-PREFIX-FUNCTION(P, m)
|
||||||
|
// q = 0 // number of characters matched
|
||||||
|
// for i = 1 to n // scan the text from left to right
|
||||||
|
// while q > 0 and P[q + 1] ≠ T[i]
|
||||||
|
// q = π[q] // next character does not match
|
||||||
|
// if P[q + 1] == T[i]
|
||||||
|
// q = q + 1 // next character matches
|
||||||
|
// if q == m // is all of P matched?
|
||||||
|
// print "Pattern occurs with shift" i - m
|
||||||
|
// q = π[q] // look for the next match
|
||||||
|
//
|
||||||
|
// COMPUTE-PREFIX-FUNCTION(P, m)
|
||||||
|
// let π[1:m] be a new array
|
||||||
|
// π[1] = 0
|
||||||
|
// k = 0
|
||||||
|
// for q = 2 to m
|
||||||
|
// while k > 0 and P[k + 1] ≠ P[q]
|
||||||
|
// k = π[k]
|
||||||
|
// if P[k + 1] == P[q]
|
||||||
|
// k = k + 1
|
||||||
|
// π[q] = k
|
||||||
|
// return π
|
||||||
|
|
||||||
|
use string_matcher_lib::StringMatcherError;
|
||||||
|
|
||||||
|
fn compute_prefix_function(pattern: &str) -> Vec<usize> {
|
||||||
|
let m = pattern.len();
|
||||||
|
|
||||||
|
let mut pi = vec![0; m];
|
||||||
|
let mut k = 0;
|
||||||
|
for q in 1..m {
|
||||||
|
let a = pattern.chars().nth(k).unwrap();
|
||||||
|
let b = pattern.chars().nth(q).unwrap();
|
||||||
|
|
||||||
|
while k > 0 && a != b {
|
||||||
|
k = pi[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = pattern.chars().nth(k).unwrap();
|
||||||
|
|
||||||
|
if a == b {
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
pi[q] = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
pi
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string_matcher(text: &str, pattern: &str) -> Result<Vec<usize>, StringMatcherError> {
|
||||||
|
let n = text.len();
|
||||||
|
let m = pattern.len();
|
||||||
|
|
||||||
|
let mut offsets = vec![];
|
||||||
|
|
||||||
|
let pi = compute_prefix_function(pattern);
|
||||||
|
let mut q = 0;
|
||||||
|
for i in 0..n {
|
||||||
|
let a = pattern.chars().nth(q).unwrap();
|
||||||
|
use StringMatcherError::*;
|
||||||
|
let b = match text.chars().nth(i) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => {
|
||||||
|
return Err(ReadError(i))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
while q > 0 && a != b {
|
||||||
|
q = pi[q - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = pattern.chars().nth(q).unwrap();
|
||||||
|
|
||||||
|
if a == b {
|
||||||
|
q += 1;
|
||||||
|
}
|
||||||
|
if q == m {
|
||||||
|
offsets.push(i + 1 - m);
|
||||||
|
q = pi[q - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(offsets)
|
||||||
|
}
|
14
l1/naive/Cargo.lock
generated
Normal file
14
l1/naive/Cargo.lock
generated
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "naive"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"string-matcher-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string-matcher-lib"
|
||||||
|
version = "0.1.0"
|
7
l1/naive/Cargo.toml
Normal file
7
l1/naive/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "naive"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
string-matcher-lib = { path = "../string-matcher-lib" }
|
28
l1/naive/src/lib.rs
Normal file
28
l1/naive/src/lib.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// CLRS 4th edition 2022
|
||||||
|
//
|
||||||
|
// NAIVE-STRING_MATCHER(T, P, n, m)
|
||||||
|
// for s = 0 to n - m
|
||||||
|
// if P[1:m] == T[s + 1:s + m]
|
||||||
|
// print "Pattern occurs with shift" s
|
||||||
|
//
|
||||||
|
// T - input text
|
||||||
|
// P - pattern
|
||||||
|
// n - input length
|
||||||
|
// m - pattern length
|
||||||
|
|
||||||
|
use string_matcher_lib::StringMatcherError;
|
||||||
|
|
||||||
|
pub fn string_matcher(text: &str, pattern: &str) -> Result<Vec<usize>, StringMatcherError> {
|
||||||
|
let n = text.len();
|
||||||
|
let m = pattern.len();
|
||||||
|
|
||||||
|
let mut offsets = vec![];
|
||||||
|
|
||||||
|
for s in 0..=(n - m) {
|
||||||
|
if pattern == &text[s..(s + m)] {
|
||||||
|
offsets.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(offsets)
|
||||||
|
}
|
7
l1/string-matcher-lib/Cargo.lock
generated
Normal file
7
l1/string-matcher-lib/Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string-matcher-lib"
|
||||||
|
version = "0.1.0"
|
6
l1/string-matcher-lib/Cargo.toml
Normal file
6
l1/string-matcher-lib/Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "string-matcher-lib"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
4
l1/string-matcher-lib/src/lib.rs
Normal file
4
l1/string-matcher-lib/src/lib.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub enum StringMatcherError {
|
||||||
|
InvalidCharacter(usize, char),
|
||||||
|
ReadError(usize),
|
||||||
|
}
|
38
l1/string-matcher/Cargo.lock
generated
Normal file
38
l1/string-matcher/Cargo.lock
generated
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fa"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"string-matcher-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kmp"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"string-matcher-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "naive"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"string-matcher-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string-matcher"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"fa",
|
||||||
|
"kmp",
|
||||||
|
"naive",
|
||||||
|
"string-matcher-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string-matcher-lib"
|
||||||
|
version = "0.1.0"
|
13
l1/string-matcher/Cargo.toml
Normal file
13
l1/string-matcher/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "string-matcher"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
string-matcher-lib = { path = "../string-matcher-lib" }
|
||||||
|
naive = { path = "../naive" }
|
||||||
|
fa = { path = "../fa" }
|
||||||
|
kmp = { path = "../kmp" }
|
83
l1/string-matcher/src/main.rs
Normal file
83
l1/string-matcher/src/main.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use std::{env, process::exit};
|
||||||
|
use string_matcher_lib::StringMatcherError::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let usage = format!(
|
||||||
|
"usage: {program} <algorithm> <pattern> <file>",
|
||||||
|
program = env::args().nth(0).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let algorithm = match env::args().nth(1) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
eprintln!("{}", usage);
|
||||||
|
exit(1)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let algorithm_char = match algorithm.to_lowercase().chars().nth(0) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
eprintln!("{}", usage);
|
||||||
|
exit(1)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let matcher = match algorithm_char {
|
||||||
|
'n' => naive::string_matcher,
|
||||||
|
'f' => fa::string_matcher,
|
||||||
|
'k' => kmp::string_matcher,
|
||||||
|
_ => {
|
||||||
|
eprintln!("{}", &usage);
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let pattern = match env::args().nth(2) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
eprintln!("{}", usage);
|
||||||
|
exit(1)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = match env::args().nth(3) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
eprintln!("{}", usage);
|
||||||
|
exit(1)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let text = match std::fs::read_to_string(&file) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("couldn't read file {}: {e}", &file);
|
||||||
|
exit(1)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match matcher(&text, &pattern) {
|
||||||
|
Ok(offsets) => {
|
||||||
|
if offsets.is_empty() {
|
||||||
|
println!("{pattern} not found in {file}");
|
||||||
|
} else {
|
||||||
|
print!("{pattern} found in {file} at the following offsets:");
|
||||||
|
|
||||||
|
for offset in offsets {
|
||||||
|
print!(" {offset}");
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("\n");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(error) => match error {
|
||||||
|
ReadError(offset) => {
|
||||||
|
eprintln!("failed reading input at offset {offset}");
|
||||||
|
},
|
||||||
|
InvalidCharacter(offset, ch) => {
|
||||||
|
eprintln!("invalid character at offset {offset}: {ch}")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue