Skip to content

Commit ac5b563

Browse files
committed
Make the dependencies optional.
1 parent 9bb28cd commit ac5b563

File tree

8 files changed

+138
-11
lines changed

8 files changed

+138
-11
lines changed

.github/workflows/test.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ name: unit_test
22
on:
33
push:
44
paths:
5+
- '.github/workflows/*'
56
- 'Cargo.toml'
67
- 'src/**.rs'
78
- 'test/**.rs'
89
pull_request:
910
paths:
11+
- '.github/workflows/*'
1012
- 'Cargo.toml'
1113
- 'src/**.rs'
1214
- 'test/**.rs'
@@ -18,11 +20,14 @@ jobs:
1820
runs-on: ubuntu-latest
1921
strategy:
2022
matrix:
23+
features: [default, fastrand, regex, ""]
2124
BUILD_TARGET: [dev, release]
25+
env:
26+
FEATURE_OPTION: ${{ matrix.features == "" && "" || "-F" }}
2227
steps:
2328
- name: Checkout
2429
uses: actions/checkout@v4
2530
- name: Build
26-
run: cargo build --profile ${{ matrix.BUILD_TARGET }}
31+
run: cargo build --profile ${{ matrix.BUILD_TARGET }} --no-default-features ${{ env.FEATURE_OPTION }} ${{ matrix.features }}
2732
- name: Test
2833
run: cargo test --profile ${{ matrix.BUILD_TARGET }}

Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ license = "MIT OR Apache-2.0"
77
repository = "https://github.com/oo13/tphrase-rs.git"
88
categories = ["internationalization", "text-processing"]
99

10+
[features]
11+
default = [ "fastrand", "regex" ]
12+
1013
[dependencies]
11-
regex = "1.11.1"
12-
fastrand = "2.3.0"
14+
fastrand = { version = "2.3.0", optional = true }
15+
regex = { version = "1.11.1", optional = true }

src/generator.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ pub type SyntaxId = usize;
8888
/// [`from_str()`]: #method.from_str
8989
#[derive(Clone, Debug)]
9090
pub struct Generator<
91-
R: RandomNumberGenerator = crate::DefaultRng,
92-
S: Substitutor = crate::DefaultSubst,
91+
#[cfg(all(feature = "fastrand", feature = "regex"))] R: RandomNumberGenerator = crate::DefaultRng,
92+
#[cfg(not(all(feature = "fastrand", feature = "regex")))] R: RandomNumberGenerator,
93+
#[cfg(feature = "regex")] S: Substitutor = crate::DefaultSubst,
94+
#[cfg(not(feature = "regex"))] S: Substitutor,
9395
> {
9496
/// The syntaxes in the instance.
9597
syntaxes: Vec<Syntax<S>>,

src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,17 @@
304304
//! gsub_limit = "g" | { ? [0-9] ? } ;
305305
//! ```
306306
//!
307+
//! # Features
308+
//! TPhrase has two features:
309+
//! - "fastrand": define [`FastrandRng`] and [`DefaultRng`]
310+
//! - "regex": define [`RegexGsub`] and [`DefaultSubst`]
311+
//!
312+
//! They are enalbed by default.
313+
//!
314+
//! Disabling the features, the default generic types of [`Generator`] is removed. 1st default generic type needs "fastrand" and "regex", 2nd needs "regex".
315+
//!
316+
//! The tests and examples support only the default features.
317+
//!
307318
//! # License
308319
//! TPhrase for Rust is licensed under either of [MIT](http://opensource.org/licenses/MIT) or [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) at your option.
309320
//!
@@ -330,15 +341,18 @@
330341
//! Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
331342
332343
mod compile_error;
344+
#[cfg(feature = "fastrand")]
333345
mod fastrand_rng;
334346
mod generator;
335347
mod parser;
336348
mod random_number_generator;
349+
#[cfg(feature = "regex")]
337350
mod regex_substitutor;
338351
mod substitutor;
339352
mod utils;
340353

341354
pub use compile_error::CompileError;
355+
#[cfg(feature = "fastrand")]
342356
pub use fastrand_rng::FastrandRng;
343357
pub use generator::Generator;
344358
pub use generator::SyntaxId;
@@ -347,15 +361,18 @@ pub use parser::data::Syntax;
347361
pub use parser::parse;
348362
pub use parser::parse_str;
349363
pub use random_number_generator::RandomNumberGenerator;
364+
#[cfg(feature = "regex")]
350365
pub use regex_substitutor::RegexGsub;
351366
pub use substitutor::Substitutor;
352367
pub use substitutor::SubstitutorAddError;
353368
pub(crate) use utils::{select_and_generate_text, TextGenerator};
354369
pub use utils::{trunc_syntax, trunc_syntax_str};
355370

356371
/// The default random number generator of [`Generator`].
372+
#[cfg(feature = "fastrand")]
357373
pub type DefaultRng = FastrandRng;
358374
/// The default substitutor of [`Generator`].
375+
#[cfg(feature = "regex")]
359376
pub type DefaultSubst = RegexGsub;
360377
/// The type of the external context.
361378
pub type ExtContext = std::collections::HashMap<String, String>;

src/parser/data.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,10 @@ impl<S: Substitutor> ProductionRule<S> {
582582
/// [`parse()`]: ../../fn.parse.html
583583
/// [`parse_str()`]: ../../fn.parse_str.html
584584
#[derive(Debug)]
585-
pub struct Syntax<S: Substitutor = crate::DefaultSubst> {
585+
pub struct Syntax<
586+
#[cfg(feature = "regex")] S: Substitutor = crate::DefaultSubst,
587+
#[cfg(not(feature = "regex"))] S: Substitutor,
588+
> {
586589
/// The assignments in the syntax.
587590
assignments: Assignments<S>,
588591
/// The reference to the start condition.

tests/test_parse.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,14 +400,14 @@ fn test_parse_gsub_with_too_big_number() {
400400

401401
#[test]
402402
fn test_parse_gsub_with_error_and_report() {
403-
let ph: Result<Generator, _> = r#"
404-
main = 1 | 2 | 3 ~ /[1/2/
403+
let ph: Result<Generator<ZeroNG, PlainSubst>, _> = r#"
404+
main = 1 | 2 | 3 ~ /1/2/3
405405
"#
406406
.parse();
407407
assert!(ph.is_err());
408408
let err = ph.err().unwrap();
409409
assert_eq!(err.error_messages().len(), 1);
410-
assert!(err.error_messages()[0].contains("Gsub error:"));
410+
assert!(err.error_messages()[0].contains("Gsub error: limit must be 0 or g."));
411411
}
412412

413413
#[test]

tests/test_struct_generator.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,3 +456,47 @@ fn test_struct_generator_clear() {
456456
assert_eq!(ph.weight(), 5.0);
457457
assert_eq!(ph.number_of_syntax(), 1);
458458
}
459+
460+
#[test]
461+
fn test_struct_generator_alternaitve_rng_and_subst() {
462+
let mut ph: Generator<PosixRng, PlainSubst> = r#"
463+
main = {= X | Y | Z } | {A} | {B}
464+
465+
A = A[1] | A[2] | A[3] ~ /[1]/[0]/g
466+
467+
B = B1 | B2 | B3
468+
"#
469+
.parse()
470+
.unwrap();
471+
let syntax: Syntax<PlainSubst> = r#"
472+
main = {= V | W } | {C} ~ /C/D/g
473+
474+
C = C1 | C2 | C3
475+
"#
476+
.parse()
477+
.unwrap();
478+
ph.add(syntax).unwrap();
479+
480+
let dist = TextDistribution::from([
481+
("X".to_string(), 1.0 / 14.0),
482+
("Y".to_string(), 1.0 / 14.0),
483+
("Z".to_string(), 1.0 / 14.0),
484+
("A[0]".to_string(), 1.0 / 14.0),
485+
("A[2]".to_string(), 1.0 / 14.0),
486+
("A[3]".to_string(), 1.0 / 14.0),
487+
("B1".to_string(), 1.0 / 14.0),
488+
("B2".to_string(), 1.0 / 14.0),
489+
("B3".to_string(), 1.0 / 14.0),
490+
("V".to_string(), 1.0 / 14.0),
491+
("W".to_string(), 1.0 / 14.0),
492+
("D1".to_string(), 1.0 / 14.0),
493+
("D2".to_string(), 1.0 / 14.0),
494+
("D3".to_string(), 1.0 / 14.0),
495+
]);
496+
497+
assert!(check_distribution(&mut ph, 100000, &dist, 0.01));
498+
499+
assert_eq!(ph.combination_number(), 14);
500+
assert_eq!(ph.weight(), 14.0);
501+
assert_eq!(ph.number_of_syntax(), 2);
502+
}

tests/utils/mod.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
use tphrase::*;
2222

23+
// Random number generators to test a phrase generation.
24+
2325
#[derive(Clone, Debug)]
2426
pub struct ZeroNG {}
2527
impl RandomNumberGenerator for ZeroNG {
@@ -97,12 +99,14 @@ pub type LinearNG3 = LinearNG<LinearNGParam3>;
9799
#[allow(dead_code)]
98100
pub type LinearNG6 = LinearNG<LinearNGParam6>;
99101

102+
// function to check a distribution.
103+
100104
#[allow(dead_code)]
101105
pub type TextDistribution = std::collections::HashMap<String, f64>;
102106

103107
#[allow(dead_code)]
104-
pub fn check_distribution(
105-
ph: &mut Generator,
108+
pub fn check_distribution<R: RandomNumberGenerator, S: Substitutor>(
109+
ph: &mut Generator<R, S>,
106110
num: usize,
107111
dist: &TextDistribution,
108112
allowance: f64,
@@ -135,3 +139,52 @@ pub fn check_distribution(
135139
}
136140
return is_match;
137141
}
142+
143+
// Alternative RNG
144+
pub struct PosixRng {
145+
n: u64,
146+
}
147+
impl RandomNumberGenerator for PosixRng {
148+
fn new() -> Self {
149+
Self { n: 1 }
150+
}
151+
fn next(self: &mut Self) -> f64 {
152+
self.n = self.n * 1103515245 + 12345;
153+
self.n &= 0xFFFF_FFFF;
154+
return ((self.n / 65536 % 32768) as f64) / 32768.0;
155+
}
156+
}
157+
158+
// Alternative Substitutor
159+
pub struct PlainSubst {
160+
replaces: Vec<(String, String)>,
161+
}
162+
impl Substitutor for PlainSubst {
163+
fn new() -> Self {
164+
Self {
165+
replaces: Vec::new(),
166+
}
167+
}
168+
fn gsub<'a>(self: &Self, s: &'a str) -> std::borrow::Cow<'a, str> {
169+
let mut r = std::borrow::Cow::Borrowed(s);
170+
for repl in self.replaces.iter() {
171+
r = r.replace(&repl.0, &repl.1).into();
172+
}
173+
return r;
174+
}
175+
fn add(
176+
self: &mut Self,
177+
pattern: &str,
178+
repl: String,
179+
limit: usize,
180+
) -> Result<(), SubstitutorAddError> {
181+
if limit == 0 {
182+
self.replaces.push((pattern.to_string(), repl));
183+
Ok(())
184+
} else {
185+
Err(SubstitutorAddError::new(
186+
"limit must be 0 or g.".to_string(),
187+
))
188+
}
189+
}
190+
}

0 commit comments

Comments
 (0)