Skip to main content

datafake_rs/operators/
fake.rs

1use crate::error::{DataFakeError, Result};
2use chrono::Utc;
3use datalogic_rs::{ContextStack, Evaluator, Operator};
4use fake::faker::address::en::{
5    CityName, CountryCode, CountryName, Latitude, Longitude, PostCode, StateAbbr, StateName,
6    StreetName, StreetSuffix, ZipCode,
7};
8use fake::faker::barcode::en::{Isbn10, Isbn13};
9use fake::faker::company::en::{
10    Bs, BsAdj, BsNoun, BsVerb, CatchPhrase, CompanyName, CompanySuffix, Industry, Profession,
11};
12use fake::faker::creditcard::en::CreditCardNumber;
13use fake::faker::currency::en::{CurrencyCode, CurrencyName, CurrencySymbol};
14use fake::faker::filesystem::en::{DirPath, FileExtension, FileName, FilePath};
15use fake::faker::finance::en::Bic;
16use fake::faker::internet::en::{
17    DomainSuffix, FreeEmail, IPv4, IPv6, MACAddress, Password, SafeEmail, UserAgent, Username,
18};
19use fake::faker::lorem::en::{Paragraph, Sentence, Word, Words};
20use fake::faker::name::en::{FirstName, LastName, NameWithTitle, Suffix, Title};
21use fake::faker::phone_number::en::{CellNumber, PhoneNumber};
22use fake::{Fake, Faker};
23use rand::Rng;
24use serde_json::Value;
25
26pub struct FakeOperator;
27
28impl Operator for FakeOperator {
29    fn evaluate(
30        &self,
31        args: &[Value],
32        _context: &mut ContextStack,
33        _evaluator: &dyn Evaluator,
34    ) -> std::result::Result<Value, datalogic_rs::Error> {
35        // Call the existing generate method and convert error
36        FakeOperator::generate(args).map_err(|e| datalogic_rs::Error::Custom(e.to_string()))
37    }
38}
39
40impl FakeOperator {
41    pub fn generate(args: &[Value]) -> Result<Value> {
42        if args.is_empty() {
43            return Err(DataFakeError::FakeOperatorError(
44                "Fake operator requires at least one argument".to_string(),
45            ));
46        }
47
48        let method = args[0].as_str().ok_or_else(|| {
49            DataFakeError::FakeOperatorError("First argument must be a string".to_string())
50        })?;
51
52        // NOTE: Locale parameter is parsed but not currently used for most operations.
53        // The underlying `fake-rs` crate has limited locale support - most generators
54        // only support English (en). The locale parameter is accepted for API compatibility
55        // and potential future expansion when fake-rs adds broader locale support.
56        let _locale = args.get(1).and_then(|v| v.as_str()).unwrap_or("en");
57
58        match method {
59            // Numeric types with optional range
60            "u8" => Self::generate_u8(args),
61            "u16" => Self::generate_u16(args),
62            "u32" => Self::generate_u32(args),
63            "u64" => Self::generate_u64(args),
64            "i8" => Self::generate_i8(args),
65            "i16" => Self::generate_i16(args),
66            "i32" => Self::generate_i32(args),
67            "i64" => Self::generate_i64(args),
68            "f32" => Self::generate_f32(args),
69            "f64" => Self::generate_f64(args),
70
71            // Boolean
72            "bool" | "boolean" => Ok(Value::Bool(rand::rng().random())),
73
74            // UUID
75            "uuid" => Ok(Value::String(fake::uuid::UUIDv4.fake())),
76
77            // Address related
78            "street_address" => {
79                // Generate a street address by combining components
80                let street_num: u16 = (1..9999).fake();
81                let street = StreetName().fake::<String>();
82                let suffix = StreetSuffix().fake::<String>();
83                Ok(Value::String(format!("{street_num} {street} {suffix}")))
84            }
85            "city" | "city_name" => Ok(Value::String(CityName().fake())),
86            "country_name" => Ok(Value::String(CountryName().fake())),
87            "country_code" => Ok(Value::String(CountryCode().fake())),
88            "state_name" => Ok(Value::String(StateName().fake())),
89            "state_abbr" => Ok(Value::String(StateAbbr().fake())),
90            "zip_code" | "zip" => Ok(Value::String(ZipCode().fake())),
91            "post_code" | "postcode" | "postal_code" => Ok(Value::String(PostCode().fake())),
92            "latitude" => Ok(Value::Number(
93                serde_json::Number::from_f64(Latitude().fake::<f64>()).unwrap(),
94            )),
95            "longitude" => Ok(Value::Number(
96                serde_json::Number::from_f64(Longitude().fake::<f64>()).unwrap(),
97            )),
98            "street_name" => Ok(Value::String(StreetName().fake())),
99            "street_suffix" => Ok(Value::String(StreetSuffix().fake())),
100
101            // Name related
102            "name" | "full_name" => {
103                // For now, use English locale only
104                use fake::faker::name::en::Name;
105                Ok(Value::String(Name().fake()))
106            }
107            "first_name" => Ok(Value::String(FirstName().fake())),
108            "last_name" => Ok(Value::String(LastName().fake())),
109            "name_with_title" => Ok(Value::String(NameWithTitle().fake())),
110            "title" => Ok(Value::String(Title().fake())),
111            "suffix" => Ok(Value::String(Suffix().fake())),
112
113            // Company related
114            "company_name" => Ok(Value::String(CompanyName().fake())),
115            "company_suffix" => Ok(Value::String(CompanySuffix().fake())),
116            "industry" => Ok(Value::String(Industry().fake())),
117            "profession" => Ok(Value::String(Profession().fake())),
118            "catch_phrase" => Ok(Value::String(CatchPhrase().fake())),
119            "bs" => Ok(Value::String(Bs().fake())),
120            "bs_adj" => Ok(Value::String(BsAdj().fake())),
121            "bs_noun" => Ok(Value::String(BsNoun().fake())),
122            "bs_verb" => Ok(Value::String(BsVerb().fake())),
123
124            // Internet related
125            "email" | "safe_email" => Ok(Value::String(SafeEmail().fake())),
126            "free_email" => Ok(Value::String(FreeEmail().fake())),
127            "username" => Ok(Value::String(Username().fake())),
128            "password" => {
129                let min_len = args.get(1).and_then(|v| v.as_u64()).unwrap_or(8) as usize;
130                let max_len = args.get(2).and_then(|v| v.as_u64()).unwrap_or(20) as usize;
131                Ok(Value::String(Password(min_len..max_len).fake()))
132            }
133            "domain_suffix" => Ok(Value::String(DomainSuffix().fake())),
134            "domain_name" => {
135                // Generate a domain name
136                let words: Vec<String> = Words(1..2).fake();
137                let suffix = DomainSuffix().fake::<String>();
138                Ok(Value::String(format!(
139                    "{}.{}",
140                    words.join("").to_lowercase(),
141                    suffix
142                )))
143            }
144            "ipv4" => Ok(Value::String(IPv4().fake())),
145            "ipv6" => Ok(Value::String(IPv6().fake())),
146            "mac_address" => Ok(Value::String(MACAddress().fake())),
147            "user_agent" => Ok(Value::String(UserAgent().fake())),
148
149            // Phone
150            "phone_number" => Ok(Value::String(PhoneNumber().fake())),
151            "cell_number" => Ok(Value::String(CellNumber().fake())),
152
153            // Finance
154            "bic" => {
155                // Support optional length parameter (8 or 11)
156                let length = args.get(1).and_then(|v| v.as_u64());
157                match length {
158                    Some(8) => Self::generate_bic_fixed(8),
159                    Some(11) => Self::generate_bic_fixed(11),
160                    Some(_) => Err(DataFakeError::FakeOperatorError(
161                        "BIC length must be 8 or 11".to_string(),
162                    )),
163                    None => Ok(Value::String(Bic().fake())), // Default: random 8 or 11
164                }
165            }
166            "bic8" => Self::generate_bic_fixed(8),
167            "bic11" => Self::generate_bic_fixed(11),
168            "credit_card_number" => Ok(Value::String(CreditCardNumber().fake())),
169
170            // Currency
171            "currency_code" => Ok(Value::String(CurrencyCode().fake())),
172            "currency_name" => Ok(Value::String(CurrencyName().fake())),
173            "currency_symbol" => Ok(Value::String(CurrencySymbol().fake())),
174
175            // Lorem
176            "word" => Ok(Value::String(Word().fake())),
177            "words" => {
178                let count = args.get(1).and_then(|v| v.as_u64()).unwrap_or(5) as usize;
179                let words: Vec<String> = Words(count..count + 1).fake();
180                Ok(Value::String(words.join(" ")))
181            }
182            "sentence" => {
183                let min_words = args.get(1).and_then(|v| v.as_u64()).unwrap_or(4) as usize;
184                let max_words = args.get(2).and_then(|v| v.as_u64()).unwrap_or(10) as usize;
185                Ok(Value::String(Sentence(min_words..max_words).fake()))
186            }
187            "paragraph" => {
188                let min_sentences = args.get(1).and_then(|v| v.as_u64()).unwrap_or(3) as usize;
189                let max_sentences = args.get(2).and_then(|v| v.as_u64()).unwrap_or(7) as usize;
190                Ok(Value::String(
191                    Paragraph(min_sentences..max_sentences).fake(),
192                ))
193            }
194
195            // Barcode
196            "isbn10" => Ok(Value::String(Isbn10().fake())),
197            "isbn13" => Ok(Value::String(Isbn13().fake())),
198
199            // Filesystem
200            "file_name" => Ok(Value::String(FileName().fake())),
201            "file_extension" => Ok(Value::String(FileExtension().fake())),
202            "dir_path" => Ok(Value::String(DirPath().fake())),
203            "file_path" => Ok(Value::String(FilePath().fake())),
204
205            // Date/Time - Generate ISO8601 formatted datetime
206            "datetime" | "iso8601_datetime" => {
207                let now = Utc::now();
208                Ok(Value::String(now.to_rfc3339()))
209            }
210            "date" => {
211                // Generate date in specified format
212                let format = args.get(1).and_then(|v| v.as_str()).unwrap_or("%Y-%m-%d");
213                let now = Utc::now();
214                Ok(Value::String(now.format(format).to_string()))
215            }
216            "time" => {
217                // Generate time in HH:MM:SS format
218                let mut rng = rand::rng();
219                let hour = rng.random_range(0..24);
220                let minute = rng.random_range(0..60);
221                let second = rng.random_range(0..60);
222                Ok(Value::String(format!("{hour:02}:{minute:02}:{second:02}")))
223            }
224            "month_name" => {
225                // Generate a month name
226                let months = [
227                    "January",
228                    "February",
229                    "March",
230                    "April",
231                    "May",
232                    "June",
233                    "July",
234                    "August",
235                    "September",
236                    "October",
237                    "November",
238                    "December",
239                ];
240                let mut rng = rand::rng();
241                let idx = rng.random_range(0..months.len());
242                Ok(Value::String(months[idx].to_string()))
243            }
244
245            // Financial - Custom types for MX messages
246            "iban" => {
247                let country = args.get(1).and_then(|v| v.as_str()).unwrap_or("DE");
248                let mut rng = rand::rng();
249                let check = format!("{:02}", rng.random_range(10..99));
250                let account: String = (0..18)
251                    .map(|_| rng.random_range(0..10).to_string())
252                    .collect();
253                Ok(Value::String(format!("{country}{check}{account}")))
254            }
255            "lei" => {
256                // Generate a realistic LEI (18 uppercase alphanumeric + 2 check digits)
257                let chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
258                let mut rng = rand::rng();
259                let lei: String = (0..18)
260                    .map(|_| chars.chars().nth(rng.random_range(0..chars.len())).unwrap())
261                    .collect();
262                let check = format!("{:02}", rng.random_range(10..99));
263                Ok(Value::String(format!("{lei}{check}")))
264            }
265            "alphanumeric" => {
266                let min_len = args.get(1).and_then(|v| v.as_u64()).unwrap_or(10) as usize;
267                let max_len = args
268                    .get(2)
269                    .and_then(|v| v.as_u64())
270                    .unwrap_or(min_len as u64) as usize;
271                let mut rng = rand::rng();
272                let len = if min_len == max_len {
273                    min_len
274                } else {
275                    rng.random_range(min_len..=max_len)
276                };
277                let chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
278                let result: String = (0..len)
279                    .map(|_| chars.chars().nth(rng.random_range(0..chars.len())).unwrap())
280                    .collect();
281                Ok(Value::String(result))
282            }
283
284            // Enum/Choice - pick one value from provided options
285            "enum" | "pick" | "choice" => {
286                if args.len() < 2 {
287                    return Err(DataFakeError::FakeOperatorError(
288                        "enum requires at least one option".to_string(),
289                    ));
290                }
291                let options = &args[1..];
292                let mut rng = rand::rng();
293                let idx = rng.random_range(0..options.len());
294                Ok(options[idx].clone())
295            }
296
297            // Regex - Handle simple regex patterns for selecting from options
298            //
299            // LIMITATION: This implementation only supports simple alternation patterns
300            // in the form "(A|B|C)" where options are separated by pipe characters.
301            // Complex regex patterns (character classes, quantifiers, anchors, etc.)
302            // are NOT supported and will return the placeholder "REGEX_PATTERN".
303            //
304            // For full regex-based generation, consider using the "enum" or "pick"
305            // methods instead, which allow explicit listing of options.
306            "regex" => {
307                if let Some(Value::String(pattern)) = args.get(1) {
308                    // Simple handling for common patterns like "(A|B|C)"
309                    if pattern.starts_with('(') && pattern.ends_with(')') {
310                        let inner = &pattern[1..pattern.len() - 1];
311                        let options: Vec<&str> = inner.split('|').collect();
312                        if !options.is_empty() {
313                            let mut rng = rand::rng();
314                            let idx = rng.random_range(0..options.len());
315                            return Ok(Value::String(options[idx].to_string()));
316                        }
317                    }
318                    // For complex patterns, return a placeholder (see LIMITATION above)
319                    Ok(Value::String("REGEX_PATTERN".to_string()))
320                } else {
321                    Err(DataFakeError::FakeOperatorError(
322                        "regex requires a pattern argument".to_string(),
323                    ))
324                }
325            }
326
327            _ => Err(DataFakeError::FakeOperatorError(format!(
328                "Unknown fake method: {method}"
329            ))),
330        }
331    }
332}
333
334/// Macro to generate numeric type handlers, reducing code duplication.
335/// Generates functions for integer types (u8, u16, u32, u64, i8, i16, i32, i64).
336macro_rules! impl_integer_generator {
337    ($fn_name:ident, $type:ty, $extract_fn:ident, $default_min:expr, $default_max:expr) => {
338        fn $fn_name(args: &[Value]) -> Result<Value> {
339            match args.len() {
340                1 => Ok(Value::Number(serde_json::Number::from(
341                    Faker.fake::<$type>(),
342                ))),
343                3 => {
344                    let min = args[1].$extract_fn().unwrap_or($default_min as _) as $type;
345                    let max = args[2].$extract_fn().unwrap_or($default_max as _) as $type;
346                    Ok(Value::Number(serde_json::Number::from(
347                        rand::rng().random_range(min..=max),
348                    )))
349                }
350                _ => Err(DataFakeError::FakeOperatorError(
351                    concat!(stringify!($type), " requires either 1 or 3 arguments").to_string(),
352                )),
353            }
354        }
355    };
356}
357
358/// Macro to generate floating-point type handlers.
359macro_rules! impl_float_generator {
360    ($fn_name:ident, $type:ty) => {
361        fn $fn_name(args: &[Value]) -> Result<Value> {
362            match args.len() {
363                1 => Ok(Value::Number(
364                    serde_json::Number::from_f64(Faker.fake::<$type>() as f64)
365                        .expect("Generated float should be a valid JSON number"),
366                )),
367                3 => {
368                    let min = args[1].as_f64().unwrap_or(0.0) as $type;
369                    let max = args[2].as_f64().unwrap_or(1.0) as $type;
370                    let value = rand::rng().random_range(min..=max);
371                    Ok(Value::Number(
372                        serde_json::Number::from_f64(value as f64)
373                            .expect("Generated float should be a valid JSON number"),
374                    ))
375                }
376                _ => Err(DataFakeError::FakeOperatorError(
377                    concat!(stringify!($type), " requires either 1 or 3 arguments").to_string(),
378                )),
379            }
380        }
381    };
382}
383
384impl FakeOperator {
385    // Generate integer type handlers using the macro
386    impl_integer_generator!(generate_u8, u8, as_u64, 0, u8::MAX);
387    impl_integer_generator!(generate_u16, u16, as_u64, 0, u16::MAX);
388    impl_integer_generator!(generate_u32, u32, as_u64, 0, u32::MAX);
389    impl_integer_generator!(generate_u64, u64, as_u64, 0, u64::MAX);
390    impl_integer_generator!(generate_i8, i8, as_i64, i8::MIN, i8::MAX);
391    impl_integer_generator!(generate_i16, i16, as_i64, i16::MIN, i16::MAX);
392    impl_integer_generator!(generate_i32, i32, as_i64, i32::MIN, i32::MAX);
393    impl_integer_generator!(generate_i64, i64, as_i64, i64::MIN, i64::MAX);
394
395    // Generate floating-point type handlers using the macro
396    impl_float_generator!(generate_f32, f32);
397    impl_float_generator!(generate_f64, f64);
398
399    fn generate_bic_fixed(length: u64) -> Result<Value> {
400        use rand::seq::IndexedRandom;
401        let mut rng = rand::rng();
402
403        // Constants for BIC generation
404        const ALPHABET: &[char; 26] = &[
405            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
406            'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
407        ];
408        const VOWELS: &[char; 5] = &['A', 'E', 'I', 'O', 'U'];
409        const ISO3166: &[&str] = &[
410            "AC", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", "AR", "AS", "AT",
411            "AU", "AW", "AX", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL",
412            "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BU", "BV", "BW", "BY", "BZ", "CA", "CC",
413            "CD", "CE", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CP", "CR", "CS",
414            "CU", "CV", "CW", "CX", "CY", "CZ", "DD", "DE", "DG", "DJ", "DK", "DM", "DO", "DZ",
415            "EA", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "EU", "FI", "FJ", "FK", "FM", "FO",
416            "FR", "FX", "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", "GM", "GN", "GP",
417            "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", "IC",
418            "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", "JE", "JM", "JO", "JP",
419            "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC",
420            "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG",
421            "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW",
422            "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NT",
423            "NU", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS",
424            "PT", "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD", "SE",
425            "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SU", "SV",
426            "SX", "SY", "SZ", "TA", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", "TL", "TM", "TN",
427            "TO", "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC",
428            "VE", "VG", "VI", "VN", "VU", "WF", "WS", "YE", "YT", "YU", "ZA", "ZM", "ZR", "ZW",
429        ];
430
431        // Generate base 8-character BIC: AAAAVCC1
432        let base_bic = format!(
433            "{}{}{}{}{}{}1",
434            *ALPHABET.choose(&mut rng).unwrap(),
435            *ALPHABET.choose(&mut rng).unwrap(),
436            *ALPHABET.choose(&mut rng).unwrap(),
437            *VOWELS.choose(&mut rng).unwrap(),
438            *ISO3166.choose(&mut rng).unwrap(),
439            *ALPHABET.choose(&mut rng).unwrap(),
440        );
441
442        let bic = if length == 11 {
443            // Add branch code suffix (3 chars)
444            let prob: i8 = rng.random_range(0..100);
445            let suffix = if prob < 70 {
446                // 70% chance: numeric branch code
447                format!(
448                    "{}{}{}",
449                    rng.random_range('0'..='9'),
450                    rng.random_range('0'..='9'),
451                    rng.random_range('0'..='9'),
452                )
453            } else {
454                // 30% chance: alphabetic branch code
455                format!(
456                    "{}{}{}",
457                    *ALPHABET.choose(&mut rng).unwrap(),
458                    *VOWELS.choose(&mut rng).unwrap(),
459                    *ALPHABET.choose(&mut rng).unwrap(),
460                )
461            };
462            format!("{}{}", base_bic, suffix)
463        } else {
464            base_bic
465        };
466
467        Ok(Value::String(bic))
468    }
469}
470
471#[cfg(test)]
472mod tests {
473    use super::*;
474    use serde_json::json;
475
476    #[test]
477    fn test_generate_uuid() {
478        let args = vec![json!("uuid")];
479        let result = FakeOperator::generate(&args).unwrap();
480        assert!(result.is_string());
481        assert_eq!(result.as_str().unwrap().len(), 36); // UUID v4 format
482    }
483
484    #[test]
485    fn test_generate_numeric_no_range() {
486        let args = vec![json!("u8")];
487        let result = FakeOperator::generate(&args).unwrap();
488        assert!(result.is_number());
489    }
490
491    #[test]
492    fn test_generate_numeric_with_range() {
493        let args = vec![json!("u8"), json!(10), json!(20)];
494        let result = FakeOperator::generate(&args).unwrap();
495        assert!(result.is_number());
496        let value = result.as_u64().unwrap();
497        assert!((10..=20).contains(&value));
498    }
499
500    #[test]
501    fn test_generate_name_with_locale() {
502        let args = vec![json!("name"), json!("en_US")];
503        let result = FakeOperator::generate(&args).unwrap();
504        assert!(result.is_string());
505        assert!(!result.as_str().unwrap().is_empty());
506    }
507
508    #[test]
509    fn test_generate_email() {
510        let args = vec![json!("email")];
511        let result = FakeOperator::generate(&args).unwrap();
512        assert!(result.is_string());
513        let email = result.as_str().unwrap();
514        assert!(email.contains('@'));
515    }
516
517    #[test]
518    fn test_generate_password_with_length() {
519        let args = vec![json!("password"), json!(10), json!(15)];
520        let result = FakeOperator::generate(&args).unwrap();
521        assert!(result.is_string());
522        let password = result.as_str().unwrap();
523        assert!(password.len() >= 10 && password.len() <= 15);
524    }
525
526    #[test]
527    fn test_invalid_method() {
528        let args = vec![json!("invalid_method")];
529        let result = FakeOperator::generate(&args);
530        assert!(result.is_err());
531    }
532
533    #[test]
534    fn test_empty_args() {
535        let args = vec![];
536        let result = FakeOperator::generate(&args);
537        assert!(result.is_err());
538    }
539
540    #[test]
541    fn test_generate_bic_default() {
542        let args = vec![json!("bic")];
543        let result = FakeOperator::generate(&args).unwrap();
544        assert!(result.is_string());
545        let bic = result.as_str().unwrap();
546        assert!(bic.len() == 8 || bic.len() == 11);
547    }
548
549    #[test]
550    fn test_generate_bic8() {
551        let args = vec![json!("bic8")];
552        let result = FakeOperator::generate(&args).unwrap();
553        assert!(result.is_string());
554        let bic = result.as_str().unwrap();
555        assert_eq!(bic.len(), 8);
556    }
557
558    #[test]
559    fn test_generate_bic11() {
560        let args = vec![json!("bic11")];
561        let result = FakeOperator::generate(&args).unwrap();
562        assert!(result.is_string());
563        let bic = result.as_str().unwrap();
564        assert_eq!(bic.len(), 11);
565    }
566
567    #[test]
568    fn test_generate_bic_with_length_8() {
569        let args = vec![json!("bic"), json!(8)];
570        let result = FakeOperator::generate(&args).unwrap();
571        assert!(result.is_string());
572        let bic = result.as_str().unwrap();
573        assert_eq!(bic.len(), 8);
574    }
575
576    #[test]
577    fn test_generate_bic_with_length_11() {
578        let args = vec![json!("bic"), json!(11)];
579        let result = FakeOperator::generate(&args).unwrap();
580        assert!(result.is_string());
581        let bic = result.as_str().unwrap();
582        assert_eq!(bic.len(), 11);
583    }
584
585    #[test]
586    fn test_generate_bic_with_invalid_length() {
587        let args = vec![json!("bic"), json!(10)];
588        let result = FakeOperator::generate(&args);
589        assert!(result.is_err());
590        match result {
591            Err(DataFakeError::FakeOperatorError(msg)) => {
592                assert!(msg.contains("BIC length must be 8 or 11"));
593            }
594            _ => panic!("Expected FakeOperatorError with specific message"),
595        }
596    }
597}