Files
crib/samples/rust.rs

407 lines
11 KiB
Rust

#![allow(dead_code)]
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::time::Duration;
//! Examples to exercise the Rust regex injection queries in the highlights.scm.
//! These cover Regex::new, regex::Regex::new, regex::bytes::Regex::new,
//! RegexSet::new, regex::RegexSet::new, RegexSetBuilder::new, and byte variants.
//!
//! Injection patterns in the query file trigger on:
//! - call to (Regex|ByteRegexBuilder)::new with a raw string literal
//! - call to (RegexSet|RegexSetBuilder)::new with an array of raw string literals
use regex::{Regex, RegexSet, RegexSetBuilder};
use regex::bytes::Regex as ByteRegex;
use regex::bytes::RegexSet as ByteRegexSet;
use regex::bytes::RegexSetBuilder as ByteRegexSetBuilder;
fn main() {
// --- Should inject (Regex::new with raw string) ---
let _simple = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
// --- Should inject (fully qualified regex::Regex::new with raw string) ---
let _fq = regex::Regex::new(r"(?m)^\w+\s*=\s*.+$").unwrap();
// --- Should inject (bytes::Regex::new with raw string) ---
let _bytes = ByteRegex::new(r"(?-u)\xFF[\x00-\x7F]+").unwrap();
// --- Should inject (RegexSet::new with array of raw strings) ---
let _set = RegexSet::new([
r"^INFO:",
r"^WARN:",
r"^ERROR:",
]).unwrap();
// --- Should inject (regex::RegexSet::new fully qualified) ---
let _set_fq = regex::RegexSet::new([
r"foo\d+",
r"bar\d+",
]).unwrap();
// --- Should inject (RegexSetBuilder::new with array of raw strings) ---
let _set_builder = RegexSetBuilder::new([
r"\bcat\b",
r"\bdog\b",
])
.case_insensitive(true)
.build()
.unwrap();
// --- Should inject (bytes set builder) ---
let _byte_set_builder = ByteRegexSetBuilder::new([
r"(?-u)\x01\x02",
r"(?-u)\xFF.+",
])
.build()
.unwrap();
// --- Should inject (bytes set) ---
let _byte_set = ByteRegexSet::new([
r"(?-u)\x00+\xFF",
r"(?-u)[\x10-\x20]+",
]).unwrap();
// --- NEGATIVE examples (should NOT inject) ---
// Not raw string literal (plain string): the query expects raw_string_literal.
let _no_inject_plain = Regex::new("plain-string-no-raw").unwrap();
// Function name is not `new`, so should not inject.
let _builder = Regex::new(r"\d+").map(|re| re.replace("123", "x"));
// Different type name, should not inject.
let _other = Some(r"not a regex call");
// Raw string but different function, should not inject.
let _format = format!(r"literal: {}", 42);
}
// Keep a simple test to ensure this compiles and runs.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke() {
let re = Regex::new(r"^\d+$").unwrap();
assert!(re.is_match("12345"));
let set = RegexSet::new([r"cat", r"dog"]).unwrap();
assert!(set.is_match("hotdog"));
}
}
/// A simple data type to exercise traits, pattern matching, and methods.
#[derive(Debug, Clone, PartialEq)]
pub struct Point {
pub x: i64,
pub y: i64,
}
impl Point {
pub fn manhattan(&self) -> i64 {
self.x.abs() + self.y.abs()
}
pub fn translate(&self, dx: i64, dy: i64) -> Self {
Self {
x: self.x + dx,
y: self.y + dy,
}
}
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
Empty,
InvalidDigit,
TooLarge,
}
pub fn parse_u8(input: &str) -> Result<u8, ParseError> {
if input.trim().is_empty() {
return Err(ParseError::Empty);
}
let mut value: u16 = 0;
for ch in input.bytes() {
if !(b'0'..=b'9').contains(&ch) {
return Err(ParseError::InvalidDigit);
}
value = value * 10 + u16::from(ch - b'0');
if value > u8::MAX as u16 {
return Err(ParseError::TooLarge);
}
}
Ok(value as u8)
}
pub fn sum_iter<I: IntoIterator<Item = i64>>(iter: I) -> i64 {
iter.into_iter().fold(0, |acc, n| acc + n)
}
pub fn split_once<'a>(input: &'a str, needle: char) -> Option<(&'a str, &'a str)> {
let idx = input.find(needle)?;
Some((&input[..idx], &input[idx + needle.len_utf8()..]))
}
pub fn join_with<I: IntoIterator<Item = String>>(iter: I, sep: &str) -> String {
let mut it = iter.into_iter().peekable();
let mut out = String::new();
while let Some(item) = it.next() {
out.push_str(&item);
if it.peek().is_some() {
out.push_str(sep);
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::sync::mpsc;
use std::thread;
macro_rules! assert_contains {
($haystack:expr, $needle:expr) => {
if !$haystack.contains($needle) {
panic!("expected {:?} to contain {:?}", $haystack, $needle);
}
};
}
#[test]
fn point_manhattan_and_display() {
let p = Point { x: -3, y: 4 };
assert_eq!(p.manhattan(), 7);
assert_eq!(p.to_string(), "(-3, 4)");
}
#[test]
fn point_translate_is_pure() {
let p = Point { x: 1, y: 2 };
let q = p.translate(3, -1);
assert_eq!(p, Point { x: 1, y: 2 });
assert_eq!(q, Point { x: 4, y: 1 });
}
#[test]
fn parse_u8_success_and_errors() {
assert_eq!(parse_u8("0"), Ok(0));
assert_eq!(parse_u8("255"), Ok(255));
assert_eq!(parse_u8(" 17 "), Ok(17)); // leading/trailing spaces are rejected as Empty? we trimmed only emptiness, digits still parsed.
assert_eq!(parse_u8(""), Err(ParseError::Empty));
assert_eq!(parse_u8(" "), Err(ParseError::Empty));
assert_eq!(parse_u8("12a"), Err(ParseError::InvalidDigit));
assert_eq!(parse_u8("256"), Err(ParseError::TooLarge));
}
#[test]
fn sum_iter_works_for_various_iterators() {
let v = vec![1, 2, 3, 4, -5];
assert_eq!(sum_iter(&v), 5);
let arr = [10i64; 4];
assert_eq!(sum_iter(arr), 40);
assert_eq!(sum_iter(0..5), 10);
}
#[test]
fn split_once_basic_and_unicode() {
assert_eq!(split_once("a,b,c", ','), Some(("a", "b,c")));
assert_eq!(split_once("no-sep", '/'), None);
// UTF-8 needle
let s = "fooλbar";
assert_eq!(split_once(s, 'λ'), Some(("foo", "bar")));
}
#[test]
fn join_with_various_lengths() {
let empty: Vec<String> = vec![];
assert_eq!(join_with(empty, ", "), "");
assert_eq!(join_with(vec!["a".into()], ", "), "a");
assert_eq!(
join_with(vec!["a".into(), "b".into(), "c".into()], "|"),
"a|b|c"
);
}
#[test]
fn hash_map_grouping_example() {
let words = ["ant", "bat", "apple", "boat"];
let mut by_initial: HashMap<char, Vec<&str>> = HashMap::new();
for w in &words {
let key = w.chars().next().unwrap();
by_initial.entry(key).or_default().push(*w);
}
assert_eq!(by_initial.get(&'a').unwrap(), &vec!["ant", "apple"]);
assert_eq!(by_initial.get(&'b').unwrap(), &vec!["bat", "boat"]);
}
#[test]
fn btree_map_sorted_iteration() {
let mut map = BTreeMap::new();
map.insert("c", 3);
map.insert("a", 1);
map.insert("b", 2);
let keys: Vec<_> = map.keys().copied().collect();
assert_eq!(keys, vec!["a", "b", "c"]);
}
#[test]
fn channels_and_threads() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
for i in 0..5 {
tx.send(i * i).unwrap();
}
});
let received: Vec<_> = (0..5).map(|_| rx.recv().unwrap()).collect();
assert_eq!(received, vec![0, 1, 4, 9, 16]);
}
#[test]
fn interior_mutability_with_refcell() {
#[derive(Debug)]
struct Counter {
inner: RefCell<u32>,
}
impl Counter {
fn inc(&self) {
*self.inner.borrow_mut() += 1;
}
fn get(&self) -> u32 {
*self.inner.borrow()
}
}
let c = Counter {
inner: RefCell::new(0),
};
c.inc();
c.inc();
assert_eq!(c.get(), 2);
}
#[test]
fn should_panic_on_too_large_parse() {
#[should_panic(expected = "TooLarge")]
fn check() {
parse_u8("999").unwrap();
}
check();
}
#[test]
fn result_based_test() -> Result<(), String> {
let p = Point { x: 2, y: 3 };
if p.manhattan() == 5 {
Ok(())
} else {
Err("manhattan distance mismatch".into())
}
}
#[test]
fn iterator_combinators_cover_common_paths() {
let data = vec![Some(1), None, Some(3), Some(4)];
let sum: i32 = data.iter().flatten().sum();
assert_eq!(sum, 8);
let doubled: Vec<_> = (1..=5).map(|n| n * 2).filter(|n| n % 4 == 0).collect();
assert_eq!(doubled, vec![4, 8]);
}
#[test]
fn pattern_matching_with_guards() {
let numbers = [-2, -1, 0, 1, 2];
let labels: Vec<_> = numbers
.iter()
.map(|n| match n {
n if *n < 0 => "neg",
0 => "zero",
n if *n % 2 == 0 => "even-pos",
_ => "odd-pos",
})
.collect();
assert_eq!(labels, vec!["neg", "neg", "zero", "odd-pos", "even-pos"]);
}
#[test]
fn custom_macro_assert_contains() {
assert_contains!("hello world", "world");
}
#[test]
fn ownership_and_borrowing_examples() {
fn takes_and_gives_back(mut v: Vec<i32>) -> Vec<i32> {
v.push(42);
v
}
let v = vec![1, 2, 3];
let v = takes_and_gives_back(v);
assert_eq!(v, vec![1, 2, 3, 42]);
let s = String::from("hi");
let len = length_of_str(&s);
assert_eq!(len, 2);
}
fn length_of_str(s: &str) -> usize {
s.len()
}
#[test]
fn lifetimes_and_slices() {
fn first<'a>(xs: &'a [i32]) -> Option<&'a i32> {
xs.first()
}
let data = [10, 20, 30];
assert_eq!(first(&data), Some(&10));
}
#[test]
fn const_generics_array_sum() {
fn sum_array<const N: usize>(arr: [i32; N]) -> i32 {
arr.iter().sum()
}
assert_eq!(sum_array::<3>([1, 2, 3]), 6);
assert_eq!(sum_array([0; 5]), 0);
}
#[test]
fn duration_and_instant_arithmetic() {
use std::time::Instant;
let start = Instant::now();
std::thread::sleep(Duration::from_millis(5));
let elapsed = start.elapsed();
assert!(elapsed >= Duration::from_millis(5));
}
#[test]
fn string_builder_patterns() {
let parts = ["a", "b", "c"];
let mut s = String::with_capacity(3);
for p in parts {
s.push_str(p);
}
assert_eq!(s, "abc");
assert!(s.capacity() >= 3);
}
#[test]
fn equality_and_ordering_on_point() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
assert_eq!(p1, p2);
assert!(p1.manhattan() <= p2.manhattan());
}
}