thirtyfour_testing_library_ext/options/
common.rs1use regex;
2use serde::{Serialize, Serializer};
3use serde_json::Value;
4
5pub trait TestingLibraryOptions: Serialize + Default {
10 fn new() -> Self
12 where
13 Self: Sized,
14 {
15 Self::default()
16 }
17
18 fn to_json_string(&self) -> Result<String, serde_json::Error> {
20 let json = serde_json::to_string(self)?;
21 Ok(process_raw_javascript_markers(&json))
23 }
24
25 fn to_json_value(&self) -> Result<Value, serde_json::Error> {
27 serde_json::to_value(self)
28 }
29}
30
31#[derive(Debug, Clone)]
33pub struct RawJavaScript(pub String);
34
35impl Serialize for RawJavaScript {
36 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37 where
38 S: Serializer,
39 {
40 let marked_value = format!("__RAW_JS__{}", self.0);
43 marked_value.serialize(serializer)
44 }
45}
46
47#[derive(Debug, Clone)]
51pub enum TextMatch {
52 String(String),
54 Regex(String),
56}
57
58impl Serialize for TextMatch {
59 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60 where
61 S: Serializer,
62 {
63 match self {
64 TextMatch::String(s) => s.serialize(serializer),
65 TextMatch::Regex(pattern) => {
66 RawJavaScript(pattern.clone()).serialize(serializer)
68 }
69 }
70 }
71}
72
73impl TextMatch {
74 pub fn validate_regex(&self) -> Result<(), String> {
76 match self {
77 TextMatch::Regex(pattern) => {
78 if !pattern.starts_with('/') {
80 return Err(
81 "Regex pattern must start with '/' (e.g., '/pattern/' or '/pattern/i')"
82 .to_string(),
83 );
84 }
85
86 let last_slash = pattern.rfind('/');
88 if last_slash.is_none() || last_slash.unwrap() == 0 {
89 return Err("Regex pattern must contain at least one '/' after the pattern (e.g., '/pattern/')".to_string());
90 }
91
92 let last_slash_pos = last_slash.unwrap();
93 let inner_pattern = &pattern[1..last_slash_pos];
94
95 regex::Regex::new(inner_pattern)
97 .map_err(|e| format!("Invalid regex pattern: {e}"))?;
98
99 Ok(())
100 }
101 _ => Ok(()),
102 }
103 }
104
105 pub fn text_value(&self) -> &str {
107 match self {
108 TextMatch::String(text) => text,
109 TextMatch::Regex(pattern) => pattern,
110 }
111 }
112
113 pub fn is_string(&self) -> bool {
115 matches!(self, TextMatch::String(_))
116 }
117
118 pub fn is_regex(&self) -> bool {
120 matches!(self, TextMatch::Regex(_))
121 }
122}
123
124impl From<&str> for TextMatch {
125 fn from(text: &str) -> Self {
126 if text.starts_with('/') && text.len() > 2 {
127 if let Some(last_slash) = text.rfind('/') {
128 if last_slash > 0 {
129 return Self::Regex(text.to_string());
130 }
131 }
132 }
133 Self::String(text.to_string())
134 }
135}
136
137impl From<String> for TextMatch {
138 fn from(text: String) -> Self {
139 TextMatch::from(text.as_str())
140 }
141}
142
143pub fn process_raw_javascript_markers(json: &str) -> String {
145 use regex::Regex;
147 let re = Regex::new(r#""__RAW_JS__([^"]+)""#).unwrap();
148 re.replace_all(json, "$1").to_string()
149}