use std::collections::HashMap;
use serde_json::{Value, json};
#[derive(Debug, Clone)]
pub struct FakeGraphQLExecutor {
data: HashMap<String, Value>,
}
impl FakeGraphQLExecutor {
pub fn new() -> Self {
let mut data = HashMap::new();
data.insert(
"users".to_string(),
json!([
{
"id": "user-1",
"name": "Alice Smith",
"email": "alice@example.com",
"active": true,
"posts": [
{
"id": "post-1",
"title": "Introduction to GraphQL",
"published": true
}
]
},
{
"id": "user-2",
"name": "Bob Jones",
"email": "bob@example.com",
"active": true,
"posts": []
},
{
"id": "user-3",
"name": "Charlie Brown",
"email": "charlie@example.com",
"active": false,
"posts": [
{
"id": "post-2",
"title": "Database Design",
"published": true
}
]
}
]),
);
data.insert(
"posts".to_string(),
json!([
{
"id": "post-1",
"title": "Introduction to GraphQL",
"content": "GraphQL is a query language...",
"published": true,
"author": {
"id": "user-1",
"name": "Alice Smith"
}
},
{
"id": "post-2",
"title": "Database Design",
"content": "JSONB is powerful...",
"published": true,
"author": {
"id": "user-3",
"name": "Charlie Brown"
}
},
{
"id": "post-3",
"title": "Draft Post",
"content": "This is a draft...",
"published": false,
"author": {
"id": "user-2",
"name": "Bob Jones"
}
}
]),
);
Self { data }
}
pub fn execute(&self, query: &str) -> Result<Value, String> {
if query.contains("{ users") {
return self.execute_users_query(query);
}
if query.contains("{ posts") {
return self.execute_posts_query(query);
}
Err(format!("Unsupported query pattern: {}", query))
}
fn execute_users_query(&self, query: &str) -> Result<Value, String> {
let users = self.data.get("users").ok_or("Users data not found")?;
let fields = self.extract_fields(query);
if fields.is_empty() {
return Err("No fields requested".to_string());
}
if let Value::Array(users_arr) = users {
let filtered_users: Vec<Value> =
users_arr.iter().map(|user| self.filter_fields(user, &fields)).collect();
Ok(json!({ "users": filtered_users }))
} else {
Err("Users data is not an array".to_string())
}
}
fn execute_posts_query(&self, query: &str) -> Result<Value, String> {
let posts = self.data.get("posts").ok_or("Posts data not found")?;
let fields = self.extract_fields(query);
if fields.is_empty() {
return Err("No fields requested".to_string());
}
if let Value::Array(posts_arr) = posts {
let filtered_posts: Vec<Value> =
posts_arr.iter().map(|post| self.filter_fields(post, &fields)).collect();
Ok(json!({ "posts": filtered_posts }))
} else {
Err("Posts data is not an array".to_string())
}
}
fn extract_fields(&self, query: &str) -> Vec<String> {
let mut fields = Vec::new();
let start = query.find('{').unwrap_or(0);
let end = query.rfind('}').unwrap_or(query.len());
let content = &query[start + 1..end];
let mut i = 0;
let chars: Vec<char> = content.chars().collect();
while i < chars.len() {
let c = chars[i];
if c.is_whitespace() {
i += 1;
continue;
}
if c == '{' || c == '}' {
i += 1;
continue;
}
let mut field = String::new();
while i < chars.len() && !chars[i].is_whitespace() && chars[i] != '{' && chars[i] != '}'
{
field.push(chars[i]);
i += 1;
}
if !field.is_empty()
&& field != "users"
&& field != "posts"
&& field != "id"
&& field != "comments"
&& !fields.contains(&field)
{
fields.push(field);
}
}
fields
}
fn filter_fields(&self, value: &Value, fields: &[String]) -> Value {
if let Value::Object(map) = value {
let mut result = serde_json::Map::new();
if let Some(id) = map.get("id") {
result.insert("id".to_string(), id.clone());
}
for field in fields {
if let Some(field_value) = map.get(field) {
result.insert(field.clone(), field_value.clone());
}
}
if fields.is_empty() {
for (key, val) in map {
result.insert(key.clone(), val.clone());
}
}
Value::Object(result)
} else {
value.clone()
}
}
}
impl Default for FakeGraphQLExecutor {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_executor_creation() {
let executor = FakeGraphQLExecutor::new();
assert!(!executor.data.is_empty());
}
#[test]
fn test_users_query() {
let executor = FakeGraphQLExecutor::new();
let query = "{ users { id name } }";
let result = executor.execute(query);
result.unwrap_or_else(|e| panic!("expected Ok executing users query: {e}"));
}
#[test]
fn test_posts_query() {
let executor = FakeGraphQLExecutor::new();
let query = "{ posts { id title } }";
let result = executor.execute(query);
result.unwrap_or_else(|e| panic!("expected Ok executing posts query: {e}"));
}
#[test]
fn test_unsupported_query() {
let executor = FakeGraphQLExecutor::new();
let query = "{ comments { id } }";
let result = executor.execute(query);
assert!(result.is_err(), "expected Err for unsupported query, got: {result:?}");
}
}