1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
//! A code surgeon.
//!
//! This crate is binary-first, but the library (what you are viewing) is a close
//! second. It is not an afterthought and is supposed to be ergonomic, well-documented,
//! well-tested, and usable by other Rust code. Refer to this crate's repository and its
//! README for (much) more information.
//!
//! For the library, much like for the binary, there are two main concepts: actions and
//! scoping. The latter are manifested in [`ScopedView`]s. Over these, on can
//! [map][`ScopedView::map`] actions. Actions are all types implementing [`Action`].
//!
//! # Examples
//!
//! A couple end-to-end examples specific to library usage are shown.
//!
//! ## Building a scoped view
//!
//! The starting point is always some [`str`] input. Over it, a [`ScopedView`] is built.
//! The latter is best constructed through a [`ScopedViewBuilder`]:
//!
//! ```rust
//! use srgn::scoping::view::ScopedViewBuilder;
//!
//! let input = "Hello, world!!";
//! let builder = ScopedViewBuilder::new(input);
//! let view = builder.build();
//! ```
//!
//! ## Exploding a scoped view
//!
//! The previous example did not achieve much of anything: neither was the view usefully
//! scoped (everything was in scope), nor was any action applied. The former is achieved
//! by, for example:
//!
//! ```rust
//! use srgn::scoping::view::ScopedViewBuilder;
//! use srgn::scoping::regex::Regex;
//! use srgn::RegexPattern;
//!
//! let input = "Hello, world!!";
//!
//! let mut builder = ScopedViewBuilder::new(input);
//!
//! let pattern = RegexPattern::new(r"[a-z]+").unwrap();
//! let scoper = Regex::new(pattern);
//!
//! builder.explode(&scoper);
//!
//! let view = builder.build();
//! ```
//!
//! ### Scoping with a language grammar
//!
//! Anything implementing [`Scoper`] is eligible for use in
//! [`ScopedViewBuilder::explode`]. This especially includes the language grammar-aware
//! types, which are [`LanguageScoper`]s. Those may be used as, for example:
//!
//! ```rust
//! use srgn::scoping::view::ScopedViewBuilder;
//! use srgn::scoping::langs::CodeQuery as CQ;
//! use srgn::scoping::langs::python::{Python, PremadePythonQuery};
//!
//! let input = "def foo(bar: int) -> int: return bar + 1 # Do a thing";
//!
//! let lang = Python::new(CQ::Premade(PremadePythonQuery::Comments));
//!
//! let mut builder = ScopedViewBuilder::new(input);
//! builder.explode(&lang);
//!
//! let mut view = builder.build();
//! view.delete();
//!
//! // Comment gone, *however* trailing whitespace remains.
//! assert_eq!(view.to_string(), "def foo(bar: int) -> int: return bar + 1 ");
//! ```
//!
//! ## Applying an action (associated function)
//!
//! With a usefully scoped view in hand, one can apply any number of actions. The
//! easiest is going through the provided associated functions directly:
//!
//! ```rust
//! use srgn::scoping::view::ScopedViewBuilder;
//! use srgn::scoping::regex::Regex;
//! use srgn::RegexPattern;
//!
//! let input = "Hello, world!!";
//!
//! let mut builder = ScopedViewBuilder::new(input);
//!
//! let pattern = RegexPattern::new(r"[a-z]+").unwrap();
//! let scoper = Regex::new(pattern);
//!
//! builder.explode(&scoper);
//!
//! let mut view = builder.build();
//! view.replace("👋".to_string());
//!
//! // All runs of lowercase letters are replaced by a single emoji.
//! assert_eq!(view.to_string(), "H👋, 👋!!");
//! ```
//!
//! Another example, using multiple actions and no scoping, is:
//!
//! ```rust
//! # #[cfg(feature = "symbols")] {
//! use srgn::scoping::view::ScopedViewBuilder;
//!
//! let input = "Assume π <= 4 < α -> β, ∀ x ∈ ℝ";
//!
//! // Short form: all `input` is in scope! No narrowing was applied.
//! let mut view = ScopedViewBuilder::new(input).build();
//! view.symbols();
//! view.upper();
//!
//! // Existing Unicode was uppercased properly, "ASCII symbols" were replaced.
//! assert_eq!(view.to_string(), "ASSUME Π ≤ 4 < Α → Β, ∀ X ∈ ℝ");
//! # }
//! ```
//!
//! ## Applying an action (passing)
//!
//! For maximum control, one can construct an action specifically and apply it that way.
//! For actions with options, this is the only way to set those options and not rely on
//! the [`Default`].
//!
//! ```rust
//! # #[cfg(feature = "german")] {
//! use srgn::scoping::view::ScopedViewBuilder;
//! use srgn::actions::German;
//!
//! let input = "Der Ueberflieger-Kaefer! 🛩️";
//!
//! let mut view = ScopedViewBuilder::new(input).build();
//! let action = German::new(true, false); // Excuse the bool ugliness.
//! view.map(&action);
//!
//! assert_eq!(view.to_string(), "Der Überflieger-Käfer! 🛩️");
//! # }
//! ```
#![warn(clippy::all)]
#![warn(clippy::pedantic)]
#![warn(clippy::cargo)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused_qualifications)]
#![warn(variant_size_differences)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![allow(clippy::multiple_crate_versions)]
#![allow(clippy::module_name_repetitions)]
#[cfg(doc)]
use crate::{
actions::Action,
scoping::{
langs::LanguageScoper,
view::{ScopedView, ScopedViewBuilder},
Scoper,
},
};
/// Main components around [`Action`]s.
pub mod actions;
/// Main components around [`ScopedView`].
pub mod scoping;
/// Pattern signalling global scope, aka matching entire inputs.
pub const GLOBAL_SCOPE: &str = r".*";
/// The type of regular expression used throughout the crate. Abstracts away the
/// underlying implementation.
pub use fancy_regex::Regex as RegexPattern;