diff --git a/src/file_reader.rs b/src/file_reader.rs index 36ce8e8..ef81c2f 100644 --- a/src/file_reader.rs +++ b/src/file_reader.rs @@ -1,3 +1,4 @@ +use crate::parsers::rng; use crate::parsers::row_parser; use crate::parsers::state::State; use crate::parsers::strategies::Strategies; @@ -22,6 +23,8 @@ pub fn read( let mut row_parser_state = State::new(); + let mut rng = rng::get(); + loop { match reader.read_line(&mut line) { Ok(bytes_read) => { @@ -29,8 +32,8 @@ pub fn read( break; } - line = line.to_string(); - let transformed_row = row_parser::parse(&line, &mut row_parser_state, strategies); + let transformed_row = + row_parser::parse(&mut rng, &line, &mut row_parser_state, strategies); file_writer.write_all(transformed_row.as_bytes())?; line.clear(); } diff --git a/src/parsers/mod.rs b/src/parsers/mod.rs index 05c4d43..0d2b2f5 100644 --- a/src/parsers/mod.rs +++ b/src/parsers/mod.rs @@ -2,6 +2,7 @@ pub mod copy_row; pub mod create_row; pub mod db_schema; pub mod national_insurance_number; +pub mod rng; pub mod row_parser; pub mod sanitiser; pub mod state; diff --git a/src/parsers/rng.rs b/src/parsers/rng.rs new file mode 100644 index 0000000..a159215 --- /dev/null +++ b/src/parsers/rng.rs @@ -0,0 +1,6 @@ +use rand::rngs::SmallRng; +use rand::SeedableRng; + +pub fn get() -> SmallRng { + SmallRng::from_rng(rand::thread_rng()).unwrap_or_else(|_| SmallRng::from_entropy()) +} diff --git a/src/parsers/row_parser.rs b/src/parsers/row_parser.rs index 32ed3d5..b86094d 100644 --- a/src/parsers/row_parser.rs +++ b/src/parsers/row_parser.rs @@ -8,6 +8,8 @@ use crate::parsers::transformer; use crate::parsers::types; use crate::parsers::types::Column; use itertools::Itertools; +use rand::rngs::SmallRng; +use std::borrow::Cow; #[derive(Debug, PartialEq)] enum RowType { @@ -38,19 +40,21 @@ fn row_type(line: &str, state: &Position) -> RowType { } } -pub fn parse(line: &str, state: &mut State, strategies: &Strategies) -> String { +pub fn parse<'line>( + rng: &mut SmallRng, + line: &'line str, + state: &mut State, + strategies: &Strategies, +) -> Cow<'line, str> { let sanitised_line = sanitiser::trim(line); - match ( - row_type(sanitised_line, &state.position), - state.position.clone(), - ) { + match (row_type(sanitised_line, &state.position), &state.position) { (RowType::CreateTableStart, _position) => { let table_name = create_row::parse(sanitised_line); state.update_position(Position::InCreateTable { table_name, types: Vec::new(), }); - line.to_string() + Cow::from(line) } ( RowType::CreateTableRow, @@ -60,34 +64,40 @@ pub fn parse(line: &str, state: &mut State, strategies: &Strategies) -> String { }, ) => { state.update_position(Position::InCreateTable { - table_name, + table_name: table_name.clone(), types: add_create_table_row_to_types(sanitised_line, current_types.to_vec()), }); - line.to_string() + Cow::from(line) } (RowType::CreateTableEnd, _position) => { state.update_position(Position::Normal); - line.to_string() + Cow::from(line) } (RowType::CopyBlockStart, _position) => { let current_table = copy_row::parse(sanitised_line, strategies); state.update_position(Position::InCopy { current_table }); - line.to_string() + Cow::from(line) } (RowType::CopyBlockEnd, _position) => { state.update_position(Position::Normal); - line.to_string() + Cow::from(line) } - (RowType::CopyBlockRow, Position::InCopy { current_table }) => { + (RowType::CopyBlockRow, Position::InCopy { ref current_table }) => { + let transformed = Cow::from(transform_row( + rng, + sanitised_line, + current_table, + &state.types, + )); state.update_position(Position::InCopy { current_table: current_table.clone(), }); - transform_row(sanitised_line, ¤t_table, &state.types) + transformed } (RowType::Normal, Position::Normal) => { state.update_position(Position::Normal); - line.to_string() + Cow::from(line) } (row_type, position) => { panic!( @@ -98,7 +108,12 @@ pub fn parse(line: &str, state: &mut State, strategies: &Strategies) -> String { } } -fn transform_row(line: &str, current_table: &CurrentTableTransforms, types: &Types) -> String { +fn transform_row( + rng: &mut SmallRng, + line: &str, + current_table: &CurrentTableTransforms, + types: &Types, +) -> String { let column_values = split_row(line); let mut transformed = column_values.enumerate().map(|(i, value)| { @@ -115,6 +130,7 @@ fn transform_row(line: &str, current_table: &CurrentTableTransforms, types: &Typ }); transformer::transform( + rng, value, column_type, ¤t_column.transformer, @@ -143,6 +159,7 @@ fn split_row(line: &str) -> std::str::Split { #[cfg(test)] mod tests { use super::*; + use crate::parsers::rng; use crate::parsers::strategy_structs::{ColumnInfo, DataCategory, TransformerType}; use crate::parsers::types::{SubType, Type}; use std::collections::HashMap; @@ -153,7 +170,8 @@ mod tests { let strategies = Strategies::new_from("public.users".to_string(), HashMap::from([])); let mut state = State::new(); - let transformed_row = parse(create_table_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, create_table_row, &mut state, &strategies); assert_eq!( state.position, Position::InCreateTable { @@ -170,7 +188,8 @@ mod tests { let strategies = Strategies::new_from("public.users".to_string(), HashMap::from([])); let mut state = State::new(); - let transformed_row = parse(create_table_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, create_table_row, &mut state, &strategies); assert_eq!( state.position, Position::InCreateTable { @@ -196,7 +215,8 @@ mod tests { }, types: Types::new(HashMap::default()), }; - let transformed_row = parse(create_table_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, create_table_row, &mut state, &strategies); assert_eq!( state.position, @@ -229,7 +249,8 @@ mod tests { }, types: Types::new(HashMap::default()), }; - let transformed_row = parse(create_table_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, create_table_row, &mut state, &strategies); assert_eq!( state.position, @@ -256,7 +277,8 @@ mod tests { }, types: Types::new(HashMap::default()), }; - let transformed_row = parse(create_table_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, create_table_row, &mut state, &strategies); assert_eq!(state.position, Position::Normal); @@ -293,7 +315,8 @@ mod tests { let strategies = Strategies::new_from("public.users".to_string(), column_infos); let mut state = State::new(); - let transformed_row = parse(copy_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, copy_row, &mut state, &strategies); assert_eq!(copy_row, transformed_row); @@ -338,7 +361,8 @@ mod tests { let strategies = Strategies::new_from("public.users".to_string(), transforms); let mut state = State::new(); - let transformed_row = parse(end_copy_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, end_copy_row, &mut state, &strategies); assert!(state.position == Position::Normal); assert_eq!(end_copy_row, transformed_row); } @@ -349,7 +373,8 @@ mod tests { let strategies = Strategies::new_from("public.users".to_string(), HashMap::new()); let mut state = State::new(); - let transformed_row = parse(non_table_data_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, non_table_data_row, &mut state, &strategies); assert!(state.position == Position::Normal); assert_eq!(non_table_data_row, transformed_row); } @@ -394,7 +419,8 @@ mod tests { .add_type("public.users", "column_3", SubType::Character) .build(), }; - let transformed_row = parse(table_data_row, &mut state, &strategies); + let mut rng = rng::get(); + let transformed_row = parse(&mut rng, table_data_row, &mut state, &strategies); assert_eq!("first\tsecond\tthird\n", transformed_row); } @@ -417,7 +443,8 @@ mod tests { .add_array_type("public.users", "column_1", SubType::Character) .build(), }; - let processed_row = parse(table_data_row, &mut state, &strategies); + let mut rng = rng::get(); + let processed_row = parse(&mut rng, table_data_row, &mut state, &strategies); assert!(table_data_row != processed_row); } } diff --git a/src/parsers/transformer.rs b/src/parsers/transformer.rs index d6526ed..16c0f8e 100644 --- a/src/parsers/transformer.rs +++ b/src/parsers/transformer.rs @@ -14,6 +14,7 @@ use fake::faker::name::en::*; use fake::Fake; use rand::SeedableRng; use rand::{rngs::SmallRng, Rng}; +use std::borrow::Cow; use std::collections::HashMap; use std::sync::atomic::{AtomicUsize, Ordering}; use uuid::Uuid; @@ -25,94 +26,94 @@ fn get_unique() -> usize { } pub fn transform<'line>( + rng: &mut SmallRng, value: &'line str, column_type: &Type, - transformer: &Transformer, + transformer: &'line Transformer, table_name: &str, -) -> String { +) -> Cow<'line, str> { if ["\\N", "deleted"].contains(&value) { - return value.to_string(); + return Cow::from(value); } if transformer.name == TransformerType::Identity { - return value.to_string(); + return Cow::from(value); } if let Array { sub_type: underlying_type, } = column_type { - return transform_array(value, underlying_type, transformer, table_name); + return transform_array(rng, value, underlying_type, transformer, table_name); } let unique = get_unique(); + match transformer.name { TransformerType::Error => { panic!("Error transform still in place for table: {}", table_name) } - TransformerType::EmptyJson => "{}".to_string(), - TransformerType::FakeBase16String => fake_base16_string(), - TransformerType::FakeBase32String => fake_base32_string(), - TransformerType::FakeCity => CityName().fake(), - TransformerType::FakeCompanyName => fake_company_name(&transformer.args, unique), - TransformerType::FakeEmail => fake_email(&transformer.args, unique), - TransformerType::FakeFirstName => FirstName().fake(), - TransformerType::FakeFullAddress => fake_full_address(), - TransformerType::FakeFullName => fake_full_name(), - TransformerType::FakeIPv4 => IPv4().fake(), - TransformerType::FakeLastName => LastName().fake(), - TransformerType::FakeNationalIdentityNumber => fake_national_identity_number(), - TransformerType::FakePostCode => fake_postcode(value), - TransformerType::FakePhoneNumber => fake_phone_number(value), - TransformerType::FakeStreetAddress => fake_street_address(), - TransformerType::FakeState => StateName().fake(), - TransformerType::FakeUsername => fake_username(&transformer.args, unique), + TransformerType::EmptyJson => Cow::from("{}"), + TransformerType::FakeBase16String => Cow::from(fake_base16_string()), + TransformerType::FakeBase32String => Cow::from(fake_base32_string()), + TransformerType::FakeCity => Cow::from(CityName().fake::()), + TransformerType::FakeCompanyName => Cow::from(fake_company_name(&transformer.args, unique)), + TransformerType::FakeEmail => Cow::from(fake_email(&transformer.args, unique)), + TransformerType::FakeFirstName => Cow::from(FirstName().fake::()), + TransformerType::FakeFullAddress => Cow::from(fake_full_address()), + TransformerType::FakeFullName => Cow::from(fake_full_name()), + TransformerType::FakeIPv4 => Cow::from(IPv4().fake::()), + TransformerType::FakeLastName => Cow::from(LastName().fake::()), + TransformerType::FakeNationalIdentityNumber => Cow::from(fake_national_identity_number()), + TransformerType::FakePostCode => Cow::from(fake_postcode(value)), + TransformerType::FakePhoneNumber => Cow::from(fake_phone_number(value)), + TransformerType::FakeStreetAddress => Cow::from(fake_street_address()), + TransformerType::FakeState => Cow::from(StateName().fake::()), + TransformerType::FakeUsername => Cow::from(fake_username(&transformer.args, unique)), //TODO not tested VV - TransformerType::FakeUUID => Uuid::new_v4().to_string(), + TransformerType::FakeUUID => Cow::from(Uuid::new_v4().to_string()), TransformerType::Fixed => fixed(&transformer.args, table_name), - TransformerType::Identity => value.to_string(), - TransformerType::ObfuscateDay => obfuscate_day(value, table_name), - TransformerType::Scramble => scramble(value), + TransformerType::Identity => Cow::from(value), + TransformerType::ObfuscateDay => Cow::from(obfuscate_day(value, table_name)), + TransformerType::Scramble => Cow::from(scramble(rng, value)), } } -fn transform_array( - value: &str, +fn transform_array<'value>( + rng: &mut SmallRng, + value: &'value str, underlying_type: &SubType, transformer: &Transformer, table_name: &str, -) -> String { +) -> Cow<'value, str> { let is_string_array = underlying_type == &SubType::Character; - let mut unsplit_array = value.to_string(); + let unsplit_array = &value[1..value.len() - 1]; let sub_type = SingleValue { sub_type: underlying_type.clone(), }; - unsplit_array.remove(0); - unsplit_array.pop(); - let array: Vec = unsplit_array + let array: Vec<_> = unsplit_array .split(", ") .map(|list_item| { if is_string_array { - let mut list_item_without_enclosing_quotes = list_item.to_string(); - list_item_without_enclosing_quotes.remove(0); - list_item_without_enclosing_quotes.pop(); + let list_item_without_enclosing_quotes = &list_item[1..list_item.len() - 1]; let transformed = transform( - &list_item_without_enclosing_quotes, + rng, + list_item_without_enclosing_quotes, &sub_type, transformer, table_name, ); - format!("\"{}\"", transformed) + Cow::from(format!("\"{}\"", transformed)) } else { - transform(list_item, &sub_type, transformer, table_name) + transform(rng, list_item, &sub_type, transformer, table_name) } }) .collect(); - return format!("{{{}}}", array.join(", ")); + Cow::from(format!("{{{}}}", array.join(", "))) } fn prepend_unique_if_present( @@ -210,7 +211,7 @@ fn fake_username(args: &Option>, unique: usize) -> Strin prepend_unique_if_present(username, args, unique) } -fn fixed(args: &Option>, table_name: &str) -> String { +fn fixed<'a>(args: &'a Option>, table_name: &str) -> Cow<'a, str> { let value = args .as_ref() .and_then(|a| a.get("value")) @@ -220,7 +221,7 @@ fn fixed(args: &Option>, table_name: &str) -> String { table_name, args, ) }); - value.to_string() + Cow::from(value) } fn obfuscate_day(value: &str, table_name: &str) -> String { @@ -247,45 +248,44 @@ fn obfuscate_day(value: &str, table_name: &str) -> String { } } -fn scramble(original_value: &str) -> String { - let mut chars = original_value.chars(); - let mut output_buf = String::with_capacity(original_value.len()); - - let mut rng = - SmallRng::from_rng(rand::thread_rng()).unwrap_or_else(|_| SmallRng::from_entropy()); - - while let Some(current_char) = chars.next() { - if current_char == '\\' { - //The string contains a control character like \t \r \n - output_buf.push(current_char); - if let Some(c) = chars.next() { - output_buf.push(c); +fn scramble(rng: &mut SmallRng, original_value: &str) -> String { + let mut last_was_backslash = false; + original_value + .chars() + .map(|c| { + if last_was_backslash { + return c; } - } else if current_char == ' ' { - output_buf.push(current_char); - } else if current_char.is_ascii_digit() { - let new_char = rng.gen_range(b'0'..=b'9') as char; - output_buf.push(new_char); - } else { - let new_char = rng.gen_range(b'a'..=b'z') as char; - output_buf.push(new_char); - } - } - output_buf + last_was_backslash = false; + if c == '\\' { + last_was_backslash = true; + c + } else if c == ' ' { + c + } else if c.is_ascii_digit() { + rng.gen_range(b'0'..=b'9') as char + } else { + rng.gen_range(b'a'..=b'z') as char + } + }) + .collect::() } #[cfg(test)] mod tests { use super::*; use crate::parsers::national_insurance_number; + use crate::parsers::rng; use regex::Regex; const TABLE_NAME: &str = "gert_lush_table"; #[test] fn null_is_not_transformed() { let null = "\\N"; + let mut rng = rng::get(); let new_null = transform( + &mut rng, null, &Type::SingleValue { sub_type: SubType::Character, @@ -302,7 +302,9 @@ mod tests { #[test] fn deleted_is_not_transformed() { let deleted = "deleted"; + let mut rng = rng::get(); let new_deleted = transform( + &mut rng, deleted, &Type::SingleValue { sub_type: SubType::Character, @@ -319,7 +321,9 @@ mod tests { #[test] fn identity() { let first_name = "any first name"; + let mut rng = rng::get(); let new_first_name = transform( + &mut rng, first_name, &Type::SingleValue { sub_type: SubType::Character, @@ -336,7 +340,9 @@ mod tests { #[test] fn fake_base16_string() { let verification_key = "1702a4eddd53d6fa79ed4a677e64c002"; + let mut rng = rng::get(); let new_verification_key = transform( + &mut rng, verification_key, &Type::SingleValue { sub_type: SubType::Character, @@ -354,7 +360,9 @@ mod tests { #[test] fn fake_base32_string() { let verification_key = "EMVXWNTUKRVAODPQ7KIBBQQTWY======"; + let mut rng = rng::get(); let new_verification_key = transform( + &mut rng, verification_key, &Type::SingleValue { sub_type: SubType::Character, @@ -372,7 +380,9 @@ mod tests { #[test] fn fake_company_name() { let company_name = "any company name"; + let mut rng = rng::get(); let new_company_name = transform( + &mut rng, company_name, &Type::SingleValue { sub_type: SubType::Character, @@ -388,15 +398,18 @@ mod tests { #[test] fn fake_company_name_with_unique_arg() { let company_name = "any company name"; + let mut rng = rng::get(); + let transformer = &Transformer { + name: TransformerType::FakeCompanyName, + args: Some(HashMap::from([("unique".to_string(), "true".to_string())])), + }; let new_company_name = transform( + &mut rng, company_name, &Type::SingleValue { sub_type: SubType::Character, }, - &Transformer { - name: TransformerType::FakeCompanyName, - args: Some(HashMap::from([("unique".to_string(), "true".to_string())])), - }, + transformer, TABLE_NAME, ); assert!(new_company_name != company_name); @@ -405,7 +418,9 @@ mod tests { #[test] fn fake_email() { let email = "any email"; + let mut rng = rng::get(); let new_email = transform( + &mut rng, email, &Type::SingleValue { sub_type: SubType::Character, @@ -429,15 +444,18 @@ mod tests { #[test] fn fake_email_with_unique_arg() { let email = "rupert@example.com"; + let mut rng = rng::get(); + let transformer = &Transformer { + name: TransformerType::FakeEmail, + args: Some(HashMap::from([("unique".to_string(), "true".to_string())])), + }; let new_email = transform( + &mut rng, email, &Type::SingleValue { sub_type: SubType::Character, }, - &Transformer { - name: TransformerType::FakeEmail, - args: Some(HashMap::from([("unique".to_string(), "true".to_string())])), - }, + transformer, TABLE_NAME, ); assert!(new_email != email); @@ -452,7 +470,9 @@ mod tests { #[test] fn fake_first_name() { let first_name = "any first name"; + let mut rng = rng::get(); let new_first_name = transform( + &mut rng, first_name, &Type::SingleValue { sub_type: SubType::Character, @@ -469,7 +489,9 @@ mod tests { #[test] fn fake_full_name() { let full_name = "any full name"; + let mut rng = rng::get(); let new_full_name = transform( + &mut rng, full_name, &Type::SingleValue { sub_type: SubType::Character, @@ -486,7 +508,9 @@ mod tests { #[test] fn fake_last_name() { let last_name = "any last name"; + let mut rng = rng::get(); let new_last_name = transform( + &mut rng, last_name, &Type::SingleValue { sub_type: SubType::Character, @@ -503,7 +527,9 @@ mod tests { #[test] fn fake_full_address() { let street_address = "any street_address"; + let mut rng = rng::get(); let new_street_address = transform( + &mut rng, street_address, &Type::SingleValue { sub_type: SubType::Character, @@ -520,7 +546,9 @@ mod tests { #[test] fn fake_national_identity_number() { let national_identity_number = "JR 55 55 55 E"; + let mut rng = rng::get(); let new_national_identity_number = transform( + &mut rng, national_identity_number, &Type::SingleValue { sub_type: SubType::Character, @@ -539,7 +567,9 @@ mod tests { #[test] fn fake_phone_number_gb() { let phone_number = "+447822222222"; + let mut rng = rng::get(); let new_phone_number = transform( + &mut rng, phone_number, &Type::SingleValue { sub_type: SubType::Character, @@ -558,7 +588,9 @@ mod tests { #[test] fn fake_phone_number_us() { let phone_number = "+16505130514"; + let mut rng = rng::get(); let new_phone_number = transform( + &mut rng, phone_number, &Type::SingleValue { sub_type: SubType::Character, @@ -577,7 +609,9 @@ mod tests { #[test] fn fake_postcode() { let postcode = "NW5 3QQ"; + let mut rng = rng::get(); let new_postcode = transform( + &mut rng, postcode, &Type::SingleValue { sub_type: SubType::Character, @@ -594,7 +628,9 @@ mod tests { #[test] fn fake_user_name() { let user_name = "any user_name"; + let mut rng = rng::get(); let new_user_name = transform( + &mut rng, user_name, &Type::SingleValue { sub_type: SubType::Character, @@ -611,15 +647,18 @@ mod tests { #[test] fn fake_user_name_supports_unique_arg() { let user_name = "any user_name"; + let mut rng = rng::get(); + let transformer = &Transformer { + name: TransformerType::FakeUsername, + args: Some(HashMap::from([("unique".to_string(), "true".to_string())])), + }; let new_user_name = transform( + &mut rng, user_name, &Type::SingleValue { sub_type: SubType::Character, }, - &Transformer { - name: TransformerType::FakeUsername, - args: Some(HashMap::from([("unique".to_string(), "true".to_string())])), - }, + transformer, TABLE_NAME, ); @@ -636,18 +675,21 @@ mod tests { fn fixed() { let url = "any web address"; let fixed_url = "a very fixed web address"; + let mut rng = rng::get(); + let transformer = &Transformer { + name: TransformerType::Fixed, + args: Some(HashMap::from([( + "value".to_string(), + fixed_url.to_string(), + )])), + }; let new_url = transform( + &mut rng, url, &Type::SingleValue { sub_type: SubType::Character, }, - &Transformer { - name: TransformerType::Fixed, - args: Some(HashMap::from([( - "value".to_string(), - fixed_url.to_string(), - )])), - }, + transformer, TABLE_NAME, ); assert_eq!(new_url, fixed_url); @@ -655,8 +697,10 @@ mod tests { #[test] #[should_panic(expected = "'value' must be present in args for a fixed transformer")] fn fixed_panics_if_value_not_provided() { + let mut rng = rng::get(); let url = "any web address"; transform( + &mut rng, url, &Type::SingleValue { sub_type: SubType::Character, @@ -672,7 +716,9 @@ mod tests { #[test] fn obfuscate_day() { let date = "2020-12-12"; + let mut rng = rng::get(); let obfuscated_date = transform( + &mut rng, date, &Type::SingleValue { sub_type: SubType::Unknown { @@ -694,7 +740,9 @@ mod tests { )] fn obfuscate_day_panics_with_invalid_date() { let date = "2020-OHMYGOSH-12"; + let mut rng = rng::get(); transform( + &mut rng, date, &Type::SingleValue { sub_type: SubType::Unknown { @@ -712,7 +760,9 @@ mod tests { #[test] fn can_deal_with_dates_from_before_christ_because_obviously_we_should_have_to() { let date = "0001-08-04 BC"; + let mut rng = rng::get(); let result = transform( + &mut rng, date, &Type::SingleValue { sub_type: SubType::Unknown { @@ -733,7 +783,9 @@ mod tests { let initial_value = "Now this is a story all about how my life got flipped turned upside down"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::SingleValue { sub_type: SubType::Character, @@ -756,7 +808,9 @@ mod tests { fn scramble_ignores_punctuation() { let initial_value = "ab.?"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::SingleValue { sub_type: SubType::Character, @@ -779,7 +833,9 @@ mod tests { fn scramble_replaces_digits_with_digits() { let initial_value = "ab 12 a1b2"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::SingleValue { sub_type: SubType::Character, @@ -802,7 +858,9 @@ mod tests { fn scramble_calculates_unicode_length_correctly() { let initial_value = "한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한한"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::SingleValue { sub_type: SubType::Character, @@ -821,7 +879,9 @@ mod tests { fn scramble_deals_with_tabs() { let initial_value = "this is a tab\t and another \t."; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::SingleValue { sub_type: SubType::Character, @@ -840,7 +900,9 @@ mod tests { fn scramble_changes_integers_into_integers_only() { let initial_value = "123456789"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::SingleValue { sub_type: SubType::Integer, @@ -863,7 +925,9 @@ mod tests { #[test] fn can_scramble_array_string_fields() { let initial_value = "{\"A\", \"B\"}"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::Array { sub_type: SubType::Character, @@ -887,7 +951,9 @@ mod tests { fn ignores_arrays_if_identity() { //TODO currently we have a couple of bugs in parsing around commas inside strings let initial_value = "{\"A, B\", \"C\"}"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::Array { sub_type: SubType::Character, @@ -904,7 +970,9 @@ mod tests { #[test] fn can_scramble_array_integer_fields() { let initial_value = "{1, 22, 444, 5656}"; + let mut rng = rng::get(); let new_value = transform( + &mut rng, initial_value, &Type::Array { sub_type: SubType::Integer, @@ -927,7 +995,9 @@ mod tests { #[test] fn empty_json() { let json = "{\"foo\": \"bar\"}"; + let mut rng = rng::get(); let new_json = transform( + &mut rng, json, &Type::SingleValue { sub_type: SubType::Character,