use crate::types::{GqlSchema, GqlType};
pub fn tokenize_identifier(s: &str) -> Vec<String> {
let mut tokens: Vec<String> = Vec::new();
let mut cur = String::new();
let chars: Vec<char> = s.chars().collect();
for (i, &c) in chars.iter().enumerate() {
if !c.is_alphanumeric() {
if !cur.is_empty() {
tokens.push(std::mem::take(&mut cur));
}
continue;
}
if !cur.is_empty() {
let prev = chars[i - 1];
let camel = (prev.is_lowercase() || prev.is_ascii_digit()) && c.is_uppercase();
let acronym_end = prev.is_uppercase()
&& c.is_uppercase()
&& chars.get(i + 1).map_or(false, |n| n.is_lowercase());
if camel || acronym_end {
tokens.push(std::mem::take(&mut cur));
}
}
cur.push(c.to_ascii_lowercase());
}
if !cur.is_empty() {
tokens.push(cur);
}
tokens
}
pub fn matches_pattern(name: &str, patterns: &[String]) -> bool {
let name_tokens = tokenize_identifier(name);
if name_tokens.is_empty() {
return false;
}
patterns.iter().any(|p| {
let pat_tokens = tokenize_identifier(p.trim());
if pat_tokens.is_empty() || pat_tokens.len() > name_tokens.len() {
return false;
}
name_tokens
.windows(pat_tokens.len())
.any(|w| w == pat_tokens.as_slice())
})
}
pub fn user_types(schema: &GqlSchema) -> Vec<&GqlType> {
schema
.types
.iter()
.filter(|t| {
t.name
.as_deref()
.map(|n| !n.starts_with("__"))
.unwrap_or(false)
})
.collect()
}
pub fn parse_extra_headers(extra_headers: &[String]) -> Vec<(String, String)> {
extra_headers
.iter()
.filter_map(|kv| {
let mut parts = kv.splitn(2, '=');
let key = parts.next().unwrap_or("").trim();
let val = parts.next().unwrap_or("").trim();
if key.is_empty() {
None
} else {
Some((key.to_string(), val.to_string()))
}
})
.collect()
}
pub fn synthesize_value(field_name: &str, type_name: &str) -> String {
let field_lower = field_name.to_lowercase();
let type_lower = type_name.to_lowercase();
match type_name {
"String" => return "\"VALUE\"".to_string(),
"Int" | "Long" | "BigInt" => return "0".to_string(),
"Float" | "Decimal" => return "0.0".to_string(),
"Boolean" => return "false".to_string(),
"ID" => return "\"ID\"".to_string(),
"Date" => return "\"2024-01-01\"".to_string(),
"DateTime" | "DateTimeOffset" | "Timestamp" | "ISO8601DateTime" => return "\"2024-01-01T00:00:00Z\"".to_string(),
"Time" => return "\"00:00:00Z\"".to_string(),
"UUID" | "GUID" => return "\"00000000-0000-0000-0000-000000000000\"".to_string(),
"URL" | "URI" => return "\"https://example.com/\"".to_string(),
"Email" => return "\"admin@example.com\"".to_string(),
"Phone" | "PhoneNumber" | "Telephone" => return "\"+15555555555\"".to_string(),
"IP" | "IPv4" | "IpAddress" => return "\"127.0.0.1\"".to_string(),
"IPv6" => return "\"::1\"".to_string(),
"JSON" | "JSONObject" | "Json" => return "{}".to_string(),
"Cents" | "Money" => return "100".to_string(),
_ => {}
}
if type_lower.contains("uuid") || type_lower.contains("guid") { return "\"00000000-0000-0000-0000-000000000000\"".to_string(); }
if type_lower.contains("email") { return "\"admin@example.com\"".to_string(); }
if type_lower.contains("url") || type_lower.contains("uri") || type_lower.contains("link") { return "\"https://example.com/\"".to_string(); }
if type_lower.contains("date") || type_lower.contains("time") || type_lower.contains("timestamp") {
if type_lower.contains("time") && !type_lower.contains("date") { return "\"00:00:00Z\"".to_string(); }
return "\"2024-01-01T00:00:00Z\"".to_string();
}
if type_lower.contains("json") || type_lower.contains("object") || type_lower.contains("map") { return "{}".to_string(); }
if type_lower.contains("ipaddress") || type_lower.contains("ipv4") || type_lower.contains("ipv6") { return "\"127.0.0.1\"".to_string(); }
if type_lower.contains("html") { return "\"<html><body>Introspectre</body></html>\"".to_string(); }
if field_lower.contains("email") { return "\"admin@example.com\"".to_string(); }
if field_lower.contains("url") || field_lower.contains("uri") || field_lower.contains("website") || field_lower.contains("link") {
return "\"https://example.com/\"".to_string();
}
if field_lower.contains("imdbid") { return "\"tt0111161\"".to_string(); }
if field_lower.contains("slug") { return "\"sample-slug\"".to_string(); }
if field_lower.contains("filename") { return "\"sample.jpg\"".to_string(); }
if field_lower.contains("password") || field_lower.contains("secret") || field_lower.contains("token") || field_lower.contains("apikey") {
return "\"REDACTED_BY_INTROSPECTRE\"".to_string();
}
if field_lower.contains("firstname") || field_lower == "firstname" { return "\"John\"".to_string(); }
if field_lower.contains("lastname") || field_lower == "lastname" { return "\"Doe\"".to_string(); }
if field_lower.contains("city") { return "\"San Francisco\"".to_string(); }
if field_lower.contains("country") { return "\"USA\"".to_string(); }
if field_lower.contains("postcode") || field_lower.contains("zipcode") { return "\"12345\"".to_string(); }
if field_lower.contains("phone") || field_lower.contains("tel") { return "\"+15555555555\"".to_string(); }
if field_lower.contains("amount") || field_lower.contains("price") || field_lower.contains("total") || field_lower.contains("count") || field_lower.contains("limit") {
return "100".to_string();
}
if field_lower.contains("currency") { return "\"USD\"".to_string(); }
if field_lower == "id" || field_lower.ends_with("id") { return "\"ID\"".to_string(); }
if field_lower.ends_with("at") || field_lower.ends_with("on") || field_lower.contains("date") { return "\"2024-01-01T00:00:00Z\"".to_string(); }
if field_lower.starts_with("is_") || field_lower.starts_with("has_") || field_lower.starts_with("can_") { return "false".to_string(); }
"null".to_string()
}