Compare commits
8 commits
b80c510d48
...
af9968947c
Author | SHA1 | Date | |
---|---|---|---|
af9968947c | |||
b864b4b388 | |||
40bdac274f | |||
889b2e2316 | |||
2ceafb7acf | |||
7a315e9cf6 | |||
475998fd22 | |||
0753c7c948 |
12 changed files with 118 additions and 84 deletions
|
@ -7,7 +7,7 @@ steps:
|
||||||
- name: linux-gnu
|
- name: linux-gnu
|
||||||
image: rust:latest
|
image: rust:latest
|
||||||
commands:
|
commands:
|
||||||
- cargo test -j3
|
- cargo test --locked -j3
|
||||||
#- name: linux-musl
|
#- name: linux-musl
|
||||||
# image: rust:alpine
|
# image: rust:alpine
|
||||||
# commands:
|
# commands:
|
||||||
|
|
|
@ -19,12 +19,12 @@ stages:
|
||||||
build:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- cargo build --verbose
|
- cargo build --verbose --locked
|
||||||
|
|
||||||
cargo-test:
|
cargo-test:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
cargo test --verbose
|
cargo test --verbose --locked
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
stage: test
|
stage: test
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
Dates are given in YYYY-MM-DD format.
|
Dates are given in YYYY-MM-DD format.
|
||||||
|
|
||||||
## v0.2
|
## v0.2
|
||||||
### v0.2.13 (2021-???)
|
### v0.2.13 (2021-04-26)
|
||||||
#### Features
|
#### Features
|
||||||
|
- Added `-v`/`--verbose` flag for setting verbosity without using `RUST_LOG`
|
||||||
- Added system extension set (`.dll`, `.so`, `.exe`...)
|
- Added system extension set (`.dll`, `.so`, `.exe`...)
|
||||||
- Output is now sorted: Files that couldn't be read, then files with no known mimetype, then files with no known
|
- Output is now sorted: Files that couldn't be read, then files with no known mimetype, then files with no known
|
||||||
extensions, then files with the wrong extension
|
extensions, then files with the wrong extension
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -193,7 +193,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fif"
|
name = "fif"
|
||||||
version = "0.2.12"
|
version = "0.2.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cached",
|
"cached",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "fif"
|
name = "fif"
|
||||||
description = "A command-line tool for detecting and optionally correcting files with incorrect extensions."
|
description = "A command-line tool for detecting and optionally correcting files with incorrect extensions."
|
||||||
version = "0.2.12"
|
version = "0.2.13"
|
||||||
authors = ["Lynnesbian <lynne@bune.city>"]
|
authors = ["Lynnesbian <lynne@bune.city>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|
45
README.md
45
README.md
|
@ -6,8 +6,10 @@ fif
|
||||||
](https://crates.io/crates/fif)
|
](https://crates.io/crates/fif)
|
||||||
[![License](https://img.shields.io/crates/l/fif.svg?style=flat-square)
|
[![License](https://img.shields.io/crates/l/fif.svg?style=flat-square)
|
||||||
](https://gitlab.com/Lynnesbian/fif/-/blob/master/LICENSE)
|
](https://gitlab.com/Lynnesbian/fif/-/blob/master/LICENSE)
|
||||||
[![Build status](https://img.shields.io/drone/build/lynnesbian/fif?logo=drone&server=https%3A%2F%2Fdrone.bune.city&style=flat-square)
|
[![Drone build status](https://img.shields.io/drone/build/lynnesbian/fif?logo=drone&server=https%3A%2F%2Fdrone.bune.city&style=flat-square)
|
||||||
](https://gitlab.com/Lynnesbian/fif/-/blob/master/README.md)
|
](https://drone.bune.city/lynnesbian/fif)
|
||||||
|
[![GitLab build status](https://img.shields.io/gitlab/pipeline/Lynnesbian/fif/master?logo=gitlab&style=flat-square)
|
||||||
|
](https://gitlab.com/Lynnesbian/fif/-/pipelines/latest)
|
||||||
[![Unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square)
|
[![Unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg?style=flat-square)
|
||||||
](https://github.com/rust-secure-code/safety-dance/)
|
](https://github.com/rust-secure-code/safety-dance/)
|
||||||
|
|
||||||
|
@ -60,16 +62,6 @@ cargo install fif --no-default-features
|
||||||
## Usage
|
## Usage
|
||||||
See `fif --help` for more.
|
See `fif --help` for more.
|
||||||
|
|
||||||
### Logging
|
|
||||||
By default, fif will log any warnings and/or errors encountered during execution. The verbosity of the logging can be
|
|
||||||
modified by the `RUST_LOG` to one of: `trace`, `debug`, `info`, `warn`, `error`.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
RUST_LOG=debug fif ~/Downloads
|
|
||||||
```
|
|
||||||
|
|
||||||
### The basics
|
### The basics
|
||||||
The simplest way to use fif looks like this:
|
The simplest way to use fif looks like this:
|
||||||
|
|
||||||
|
@ -106,3 +98,32 @@ You can also manually specify an output format to use:
|
||||||
```bash
|
```bash
|
||||||
fif -O powershell ~/Documents > output.ps1
|
fif -O powershell ~/Documents > output.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
By default, fif will log any warnings and/or errors encountered during execution. This can be changed with the `-v`
|
||||||
|
flag:
|
||||||
|
```bash
|
||||||
|
# also log info
|
||||||
|
fif -v ~/Downloads
|
||||||
|
# ...and debug
|
||||||
|
fif -vv ~/Downloads
|
||||||
|
# ...and trace
|
||||||
|
fif -vvv ~/Downloads
|
||||||
|
```
|
||||||
|
The verbosity of the logging can be
|
||||||
|
modified by the `RUST_LOG` to one of: `trace`, `debug`, `info`, `warn`, `error`.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RUST_LOG=debug fif ~/Downloads
|
||||||
|
```
|
||||||
|
|
||||||
|
The five logging levels are used as follows:
|
||||||
|
| Level | Description | Example |
|
||||||
|
|-|-|-|
|
||||||
|
| error | Errors that cause fif to stop running | fif was unable to open the provided directory |
|
||||||
|
| warn | Warnings that don't cause fif to stop running | fif was unable to determine the mime type of a given file |
|
||||||
|
| info | Information pertaining to fif's status | The provided directory was scanned without issue, and no files are in need of renaming |
|
||||||
|
| debug | Debug information - usually not important to end users | The list of extensions fif will consider |
|
||||||
|
| trace | Trace info - usually not important to end users | "Found 15 items to check", "Scan successful", etc. |
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
fd -e rs -x touch {}
|
fd -e rs -x touch {}
|
||||||
cargo clippy --tests -- \
|
cargo clippy --all-features -- \
|
||||||
-W clippy::nursery \
|
-W clippy::nursery \
|
||||||
-W clippy::perf \
|
-W clippy::perf \
|
||||||
-W clippy::pedantic \
|
-W clippy::pedantic \
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
//! Sets of extensions for use with [Parameter](crate::parameters::Parameters)'s `-E` flag.
|
|
||||||
use clap::Clap;
|
|
||||||
|
|
||||||
#[derive(Clap, PartialEq, Debug)]
|
|
||||||
pub enum ExtensionSet {
|
|
||||||
/// Extensions used for image file formats, such as `png`, `jpeg`, `webp`, etc.
|
|
||||||
Images,
|
|
||||||
/// Extensions used for audio file formats, such as `mp3`, `ogg`, `flac`, etc.
|
|
||||||
Audio,
|
|
||||||
/// Extensions used for video file formats, such as `mkv`, `mp4`, `mov`, etc.
|
|
||||||
Videos,
|
|
||||||
/// Extensions used for media file formats. This acts as a combination of the [Images](ExtensionSet::Images),
|
|
||||||
/// [Audio](ExtensionSet::Audio) and [Videos](ExtensionSet::Videos) variants.
|
|
||||||
Media,
|
|
||||||
/// Extensions used for document file formats, such as `pdf`, `odt`, `docx`, etc.
|
|
||||||
Documents,
|
|
||||||
/// Extensions used for text file formats, such as `txt`, `toml`, `html`, etc.
|
|
||||||
Text,
|
|
||||||
/// Extensions used for archive file formats, such as `zip`, `zst`, `gz`, etc.
|
|
||||||
Archives,
|
|
||||||
/// Extensions used for system file formats, such as `mbr`, `crash`, `dll`, etc.
|
|
||||||
System,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtensionSet {
|
|
||||||
/// The list of known extensions for this `ExtensionSet`.
|
|
||||||
pub fn extensions(&self) -> Vec<&str> {
|
|
||||||
match self {
|
|
||||||
Self::Images => mime_guess::get_mime_extensions_str("image/*").unwrap().to_vec(),
|
|
||||||
Self::Audio => mime_guess::get_mime_extensions_str("audio/*").unwrap().to_vec(),
|
|
||||||
Self::Videos => mime_guess::get_mime_extensions_str("video/*").unwrap().to_vec(),
|
|
||||||
Self::Media => [
|
|
||||||
Self::Images.extensions(),
|
|
||||||
Self::Audio.extensions(),
|
|
||||||
Self::Videos.extensions(),
|
|
||||||
]
|
|
||||||
.concat(),
|
|
||||||
Self::Documents => vec![
|
|
||||||
"pdf", "doc", "docx", "ppt", "pptx", "xls", "xlsx", "csv", "tsv", "odt", "ods", "odp", "oda", "rtf", "ps",
|
|
||||||
"pages", "key", "numbers",
|
|
||||||
],
|
|
||||||
Self::Text => mime_guess::get_mime_extensions_str("text/*").unwrap().to_vec(),
|
|
||||||
// many compressed file types follow the name scheme "application/x.+compressed.*" - maybe this can be used
|
|
||||||
// somehow to extract extensions for compressed files from mime_guess?
|
|
||||||
Self::Archives => vec!["zip", "tar", "gz", "zst", "xz", "rar", "7z", "bz", "bz2", "tgz", "rpa"],
|
|
||||||
Self::System => vec![
|
|
||||||
"com", "dll", "exe", "sys", "reg", "nt", "cpl", "msi", "efi", "bio", "rcv", "mbr", "sbf", "grub", "ko",
|
|
||||||
"dylib", "pdb", "hdmp", "crash",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
13
src/main.rs
13
src/main.rs
|
@ -27,8 +27,6 @@ use clap::Clap;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
#[cfg(feature = "multi-threaded")]
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
use crate::findings::Findings;
|
use crate::findings::Findings;
|
||||||
|
@ -37,7 +35,6 @@ use crate::mime_db::MimeDb;
|
||||||
use crate::parameters::{OutputFormat, ScanOpts};
|
use crate::parameters::{OutputFormat, ScanOpts};
|
||||||
use crate::scan_error::ScanError;
|
use crate::scan_error::ScanError;
|
||||||
|
|
||||||
mod extension_set;
|
|
||||||
mod findings;
|
mod findings;
|
||||||
mod formats;
|
mod formats;
|
||||||
mod inspectors;
|
mod inspectors;
|
||||||
|
@ -67,7 +64,7 @@ cfg_if! {
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: parameters::Parameters = parameters::Parameters::parse();
|
let args: parameters::Parameters = parameters::Parameters::parse();
|
||||||
|
|
||||||
let mut builder = env_logger::Builder::from_env(Env::new().filter_or("RUST_LOG", "INFO"));
|
let mut builder = env_logger::Builder::from_env(Env::new().filter_or("RUST_LOG", args.default_verbosity()));
|
||||||
|
|
||||||
builder
|
builder
|
||||||
// .format(|buf, r| writeln!(buf, "{} - {}", r.level(), r.args()))
|
// .format(|buf, r| writeln!(buf, "{} - {}", r.level(), r.args()))
|
||||||
|
@ -115,13 +112,13 @@ fn main() {
|
||||||
match result {
|
match result {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
debug!(
|
debug!(
|
||||||
"{:?} is {}, should have file extension {}",
|
"{:?} is of type {}, should have extension \"{}\"",
|
||||||
r.file,
|
r.file,
|
||||||
r.mime,
|
r.mime,
|
||||||
r.recommended_extension().unwrap_or_else(|| "???".into())
|
r.recommended_extension().unwrap_or_else(|| "???".into())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Err(f) => warn!("Error 0uo - {}", f),
|
Err(f) => warn!("{}", f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,14 +220,12 @@ fn scan_file(entry: &DirEntry) -> Result<Findings, ScanError> {
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
// an error occurred while trying to read the file
|
// an error occurred while trying to read the file
|
||||||
// error!("{}: {}", entry.path().to_string_lossy(), error);
|
|
||||||
return Err(ScanError::File(entry.path()));
|
return Err(ScanError::File(entry.path()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
if result.is_none() {
|
if result.is_none() {
|
||||||
// the file was read successfully, but we were unable to determine its mimetype
|
// the file was read successfully, but we were unable to determine its mimetype
|
||||||
// warn!("Couldn't determine mimetype for {}", entry.path().to_string_lossy());
|
|
||||||
return Err(ScanError::Mime(entry.path()));
|
return Err(ScanError::Mime(entry.path()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,6 +254,8 @@ fn scan_file(entry: &DirEntry) -> Result<Findings, ScanError> {
|
||||||
fn scan_from_walkdir(entries: &[DirEntry]) -> Vec<Result<Findings, ScanError>> {
|
fn scan_from_walkdir(entries: &[DirEntry]) -> Vec<Result<Findings, ScanError>> {
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(feature = "multi-threaded")] {
|
if #[cfg(feature = "multi-threaded")] {
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
// rather than using a standard par_iter, split the entries into chunks of 32 first.
|
// rather than using a standard par_iter, split the entries into chunks of 32 first.
|
||||||
// this allows each spawned thread to handle 32 files before before closing, rather than creating a new thread for
|
// this allows each spawned thread to handle 32 files before before closing, rather than creating a new thread for
|
||||||
// each file. this leads to a pretty substantial speedup that i'm pretty substantially happy about 0u0
|
// each file. this leads to a pretty substantial speedup that i'm pretty substantially happy about 0u0
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! [Clap] struct used to parse command line arguments.
|
//! [Clap] struct used to parse command line arguments.
|
||||||
|
|
||||||
use crate::extension_set::ExtensionSet;
|
|
||||||
use crate::string_type::String as StringType;
|
use crate::string_type::String as StringType;
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use clap::{AppSettings, Clap};
|
use clap::{AppSettings, Clap};
|
||||||
|
@ -72,6 +71,11 @@ pub struct Parameters {
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pub follow_symlinks: bool,
|
pub follow_symlinks: bool,
|
||||||
|
|
||||||
|
/// Output verbosity. Defaults to only logging warnings and errors.
|
||||||
|
/// Can be overridden by RUST_LOG.
|
||||||
|
#[clap(short, long, parse(from_occurrences))]
|
||||||
|
pub verbose: u8,
|
||||||
|
|
||||||
/// The directory to process.
|
/// The directory to process.
|
||||||
// TODO: right now this can only take a single directory - should this be improved?
|
// TODO: right now this can only take a single directory - should this be improved?
|
||||||
#[clap(name = "DIR", default_value = ".", parse(from_os_str))]
|
#[clap(name = "DIR", default_value = ".", parse(from_os_str))]
|
||||||
|
@ -110,4 +114,67 @@ impl Parameters {
|
||||||
follow_symlinks: self.follow_symlinks,
|
follow_symlinks: self.follow_symlinks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_verbosity(&self) -> &'static str {
|
||||||
|
#![allow(clippy::missing_const_for_fn)]
|
||||||
|
// match was not permitted inside const functions until 1.46
|
||||||
|
|
||||||
|
match self.verbose {
|
||||||
|
0 => "warn",
|
||||||
|
1 => "info",
|
||||||
|
2 => "debug",
|
||||||
|
_ => "trace",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets of extensions for use with [Parameter](crate::parameters::Parameters)'s `-E` flag.
|
||||||
|
#[derive(Clap, PartialEq, Debug)]
|
||||||
|
pub enum ExtensionSet {
|
||||||
|
/// Extensions used for image file formats, such as `png`, `jpeg`, `webp`, etc.
|
||||||
|
Images,
|
||||||
|
/// Extensions used for audio file formats, such as `mp3`, `ogg`, `flac`, etc.
|
||||||
|
Audio,
|
||||||
|
/// Extensions used for video file formats, such as `mkv`, `mp4`, `mov`, etc.
|
||||||
|
Videos,
|
||||||
|
/// Extensions used for media file formats. This acts as a combination of the [Images](ExtensionSet::Images),
|
||||||
|
/// [Audio](ExtensionSet::Audio) and [Videos](ExtensionSet::Videos) variants.
|
||||||
|
Media,
|
||||||
|
/// Extensions used for document file formats, such as `pdf`, `odt`, `docx`, etc.
|
||||||
|
Documents,
|
||||||
|
/// Extensions used for text file formats, such as `txt`, `toml`, `html`, etc.
|
||||||
|
Text,
|
||||||
|
/// Extensions used for archive file formats, such as `zip`, `zst`, `gz`, etc.
|
||||||
|
Archives,
|
||||||
|
/// Extensions used for system file formats, such as `mbr`, `crash`, `dll`, etc.
|
||||||
|
System,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtensionSet {
|
||||||
|
/// The list of known extensions for this `ExtensionSet`.
|
||||||
|
pub fn extensions(&self) -> Vec<&str> {
|
||||||
|
match self {
|
||||||
|
Self::Images => mime_guess::get_mime_extensions_str("image/*").unwrap().to_vec(),
|
||||||
|
Self::Audio => mime_guess::get_mime_extensions_str("audio/*").unwrap().to_vec(),
|
||||||
|
Self::Videos => mime_guess::get_mime_extensions_str("video/*").unwrap().to_vec(),
|
||||||
|
Self::Media => [
|
||||||
|
Self::Images.extensions(),
|
||||||
|
Self::Audio.extensions(),
|
||||||
|
Self::Videos.extensions(),
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
|
Self::Documents => vec![
|
||||||
|
"pdf", "doc", "docx", "ppt", "pptx", "xls", "xlsx", "csv", "tsv", "odt", "ods", "odp", "oda", "rtf", "ps",
|
||||||
|
"pages", "key", "numbers",
|
||||||
|
],
|
||||||
|
Self::Text => mime_guess::get_mime_extensions_str("text/*").unwrap().to_vec(),
|
||||||
|
// many compressed file types follow the name scheme "application/x.+compressed.*" - maybe this can be used
|
||||||
|
// somehow to extract extensions for compressed files from mime_guess?
|
||||||
|
Self::Archives => vec!["zip", "tar", "gz", "zst", "xz", "rar", "7z", "bz", "bz2", "tgz", "rpa"],
|
||||||
|
Self::System => vec![
|
||||||
|
"com", "dll", "exe", "sys", "reg", "nt", "cpl", "msi", "efi", "bio", "rcv", "mbr", "sbf", "grub", "ko",
|
||||||
|
"dylib", "pdb", "hdmp", "crash",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,7 +278,7 @@ fn outputs_move_commands() {
|
||||||
#[test]
|
#[test]
|
||||||
/// Ensure that the Media extension set contains all (is a superset) of Audio, Video, and Images.
|
/// Ensure that the Media extension set contains all (is a superset) of Audio, Video, and Images.
|
||||||
fn media_contains_audio_video_images() {
|
fn media_contains_audio_video_images() {
|
||||||
use crate::extension_set::ExtensionSet::{Audio, Images, Media, Videos};
|
use crate::parameters::ExtensionSet::{Audio, Images, Media, Videos};
|
||||||
let media_exts = Media.extensions();
|
let media_exts = Media.extensions();
|
||||||
|
|
||||||
// assert every extension in the audio/video/image sets is contained in the media set
|
// assert every extension in the audio/video/image sets is contained in the media set
|
||||||
|
|
6
test.py
6
test.py
|
@ -11,7 +11,7 @@ def test_archs():
|
||||||
|
|
||||||
for arch in archs:
|
for arch in archs:
|
||||||
print(f"Testing {arch} ({upto} of {target})")
|
print(f"Testing {arch} ({upto} of {target})")
|
||||||
subprocess.run(f"cross test --features=infer-backend --target {arch}-unknown-linux-gnu".split(" "))
|
subprocess.run(f"cross test --features=infer-backend --target {arch}-unknown-linux-gnu".split(" ")).check_returncode()
|
||||||
upto += 1
|
upto += 1
|
||||||
|
|
||||||
def test_versions():
|
def test_versions():
|
||||||
|
@ -33,11 +33,11 @@ def test_versions():
|
||||||
for version in versions:
|
for version in versions:
|
||||||
for backend in backends:
|
for backend in backends:
|
||||||
print(f"[{version}, {backend}] Tests ({upto} of {target})")
|
print(f"[{version}, {backend}] Tests ({upto} of {target})")
|
||||||
subprocess.run(f"cargo +{version} test --features={backend}-backend".split(" "))
|
subprocess.run(f"cargo +{version} test --features={backend}-backend".split(" ")).check_returncode()
|
||||||
upto += 1
|
upto += 1
|
||||||
|
|
||||||
print(f"[{version}, {backend}] Scanning imgs ({upto} of {target})")
|
print(f"[{version}, {backend}] Scanning imgs ({upto} of {target})")
|
||||||
subprocess.run(f"cargo +{version} run --release --features={backend}-backend -- imgs".split(" "))
|
subprocess.run(f"cargo +{version} run --release --features={backend}-backend -- imgs".split(" ")).check_returncode()
|
||||||
upto += 1
|
upto += 1
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
Loading…
Reference in a new issue