srgn/
lib.rs

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