mirror of
https://github.com/chmln/sd
synced 2024-11-21 10:43:04 +00:00
start
This commit is contained in:
commit
db3fc2fcff
8 changed files with 413 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
**/*.rs.bk
|
212
Cargo.lock
generated
Normal file
212
Cargo.lock
generated
Normal file
|
@ -0,0 +1,212 @@
|
|||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.45 (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 = "re"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.1"
|
||||
source = "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 = "winapi"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
||||
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
|
||||
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
|
||||
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
|
||||
"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
|
||||
"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
|
||||
"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||
"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"
|
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "re"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
authors = ["Gregory <gregory.mkv@gmail.com>"]
|
||||
description = "A find & replace CLI focused on user-friendliness"
|
||||
keywords = ["sed", "find", "replace", "regex"]
|
||||
license = "MIT"
|
||||
homepage = "https://github.com/chmln/re"
|
||||
repository = "https://github.com/chmln/re.git"
|
||||
categories = ["command-line-utilities", "text-processing", "development-tools"]
|
||||
|
||||
[dependencies]
|
||||
clap = "2.32.0"
|
||||
regex = "1.1.0"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
debug = false
|
56
src/app.rs
Normal file
56
src/app.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use {crate::{Error,Stream,Source}};
|
||||
|
||||
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub(crate) struct App;
|
||||
|
||||
impl App {
|
||||
pub(crate) fn run() -> Result<(), Error> {
|
||||
use clap;
|
||||
let app = clap::App::new("re")
|
||||
.version(VERSION)
|
||||
.setting(clap::AppSettings::ColoredHelp)
|
||||
.setting(clap::AppSettings::NextLineHelp)
|
||||
.arg(
|
||||
clap::Arg::with_name("enable_regex")
|
||||
.short("r")
|
||||
.long("regex")
|
||||
.required(false)
|
||||
.takes_value(false)
|
||||
.help("Enable regex"),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::with_name("input")
|
||||
.short("i")
|
||||
.required(false)
|
||||
.takes_value(true)
|
||||
.help("The path to file")
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::with_name("find")
|
||||
.help("The string or regexp (if --regex) to search for.")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::with_name("replace_with")
|
||||
.help("What to replace each match with. If regex is enabled, you may use captured values like $1, $2, etc.")
|
||||
.required(true)
|
||||
.index(2),
|
||||
);
|
||||
|
||||
let matches = app.get_matches();
|
||||
|
||||
let file_path = matches.value_of("input").map(|p| p.to_string());
|
||||
let find = matches.value_of("find").unwrap();
|
||||
let replace_with = matches.value_of("replace_with").unwrap();
|
||||
let is_regex = matches.occurrences_of("enable_regex") == 1;
|
||||
|
||||
let source = Source::from(file_path);
|
||||
let mut stream: Stream = (&source).into_stream()?;
|
||||
stream.replace(is_regex, find, replace_with)?;
|
||||
|
||||
// replace file in-place, or pipe to stdout
|
||||
stream.output(&source)
|
||||
}
|
||||
}
|
32
src/error.rs
Normal file
32
src/error.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
pub(crate) struct Error {
|
||||
// low-level cause, used only for debugging
|
||||
cause: String,
|
||||
// user-facing error output
|
||||
description: String,
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error
|
||||
where
|
||||
T: std::error::Error,
|
||||
{
|
||||
fn from(err: T) -> Error {
|
||||
Error {
|
||||
cause: err
|
||||
.source()
|
||||
.map_or_else(|| "N/A".to_string(), |e| e.to_string()),
|
||||
description: format!("{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", self.description)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "Error: {}\nDetails: {}", self.description, self.cause)
|
||||
}
|
||||
}
|
78
src/input.rs
Normal file
78
src/input.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use {crate::Error, std::{fs::File, io::prelude::*}};
|
||||
|
||||
pub(crate) enum Source {
|
||||
Stdin,
|
||||
File(String),
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub(crate) fn from(maybe_path: Option<String>) -> Self {
|
||||
maybe_path
|
||||
.map(Source::File)
|
||||
.unwrap_or_else(|| Source::Stdin)
|
||||
}
|
||||
|
||||
pub(crate) fn into_stream(&self) -> Result<Stream, Error> {
|
||||
match self {
|
||||
Source::Stdin => {
|
||||
let mut buffer = String::new();
|
||||
let stdin = std::io::stdin();
|
||||
let mut handle = stdin.lock();
|
||||
|
||||
handle.read_to_string(&mut buffer)?;
|
||||
Ok(Stream::new(buffer))
|
||||
}
|
||||
Source::File(path) => {
|
||||
let file = File::open(path)?;
|
||||
let mut buf_reader = std::io::BufReader::new(file);
|
||||
let mut buffer = String::new();
|
||||
buf_reader.read_to_string(&mut buffer)?;
|
||||
Ok(Stream::new(buffer))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub(crate) struct Stream {
|
||||
data: String,
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
pub(crate) fn new(data: String) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
|
||||
pub(crate) fn replace(
|
||||
&mut self,
|
||||
is_regex: bool,
|
||||
look_for: &str,
|
||||
replace_with: &str,
|
||||
) -> Result<(), crate::Error> {
|
||||
if is_regex {
|
||||
self.data = regex::Regex::new(look_for)?
|
||||
.replace_all(&self.data, replace_with)
|
||||
.to_string()
|
||||
} else {
|
||||
self.data = self.data.replace(look_for, replace_with)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Output based on input.
|
||||
// When dealing with a file, transform it in-place.
|
||||
// Otherwise, pipe to stdout.
|
||||
pub(crate) fn output(self, source: &Source) -> Result<(), crate::Error> {
|
||||
match source {
|
||||
Source::File(path) => {
|
||||
Ok(std::fs::write(path, self.data)?)
|
||||
},
|
||||
Source::Stdin => {
|
||||
let stdout = std::io::stdout();
|
||||
let mut handle = stdout.lock();
|
||||
handle.write(self.data.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
src/main.rs
Normal file
12
src/main.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
mod app;
|
||||
mod error;
|
||||
mod input;
|
||||
|
||||
pub(crate) use {
|
||||
crate::error::Error,
|
||||
crate::input::{Source, Stream},
|
||||
};
|
||||
|
||||
fn main() -> Result<(), ()> {
|
||||
app::App::run().map_err(|e| eprintln!("{}", e))
|
||||
}
|
1
src/utils.rs
Normal file
1
src/utils.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
Loading…
Reference in a new issue