use anyhow::Result;
use image::{DynamicImage, GenericImageView};
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ImageType {
ArchitectureDiagram,
UIMock,
Whiteboard,
Screenshot,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DesignSpec {
pub content: String,
pub image_type: ImageType,
pub detected_elements: Vec<DetectedElement>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectedElement {
pub element_type: String,
pub content: String,
pub position: (u32, u32), pub size: (u32, u32), }
pub struct VisionAdapter {
pub image_processor: ImageProcessor,
}
impl VisionAdapter {
pub fn new() -> Self {
Self {
image_processor: ImageProcessor::new(),
}
}
pub async fn interpret_design(&self, image_path: &str) -> Result<DesignSpec> {
let image = self.image_processor.load_image(image_path)?;
let image_type = self.classify_image(&image, image_path).await?;
match image_type {
ImageType::ArchitectureDiagram => self.extract_components(image_path).await,
ImageType::UIMock => self.generate_html_css(image_path).await,
ImageType::Whiteboard => self.parse_sketch(image_path).await,
ImageType::Screenshot => self.interpret_screenshot(image_path).await,
ImageType::Unknown => {
self.generate_code_from_visual_elements(image_path).await
}
}
}
async fn classify_image(&self, image: &DynamicImage, _image_path: &str) -> Result<ImageType> {
let (width, height) = image.dimensions();
let aspect_ratio = width as f32 / height as f32;
let has_boxes_and_arrows = self.has_boxes_and_arrows(image)?;
let has_ui_elements = self.has_ui_elements(image)?;
let has_handwriting = self.has_handwriting(image)?;
if has_boxes_and_arrows {
Ok(ImageType::ArchitectureDiagram)
} else if has_ui_elements {
Ok(ImageType::UIMock)
} else if has_handwriting {
Ok(ImageType::Whiteboard)
} else {
Ok(ImageType::Unknown)
}
}
async fn extract_components(&self, image_path: &str) -> Result<DesignSpec> {
let detected_elements = vec![
DetectedElement {
element_type: "database".to_string(),
content: "User Database".to_string(),
position: (100, 100),
size: (80, 40),
},
DetectedElement {
element_type: "service".to_string(),
content: "Auth Service".to_string(),
position: (250, 100),
size: (80, 40),
},
DetectedElement {
element_type: "service".to_string(),
content: "API Gateway".to_string(),
position: (400, 100),
size: (80, 40),
},
];
let spec = DesignSpec {
content: format!("Architecture diagram interpreted from: {}", image_path),
image_type: ImageType::ArchitectureDiagram,
detected_elements,
};
Ok(spec)
}
async fn generate_html_css(&self, image_path: &str) -> Result<DesignSpec> {
let html_template = r#"
<!DOCTYPE html>
<html>
<head>
<title>Generated UI</title>
<style>
body { font-family: Arial, sans-serif; }
.container { max-width: 800px; margin: 0 auto; }
</style>
</head>
<body>
<div class="container">
<h1>Generated from UI mock: {}</h1>
<p>UI elements would be generated based on visual analysis</p>
</div>
</body>
</html>
"#
.trim();
let content = html_template.replace("{}", image_path);
let detected_elements = vec![
DetectedElement {
element_type: "header".to_string(),
content: "Header element detected".to_string(),
position: (0, 0),
size: (800, 60),
},
DetectedElement {
element_type: "content".to_string(),
content: "Main content area".to_string(),
position: (0, 60),
size: (800, 400),
},
];
let spec = DesignSpec {
content,
image_type: ImageType::UIMock,
detected_elements,
};
Ok(spec)
}
async fn parse_sketch(&self, image_path: &str) -> Result<DesignSpec> {
let detected_elements = vec![
DetectedElement {
element_type: "text".to_string(),
content: "Login page".to_string(),
position: (50, 50),
size: (100, 20),
},
DetectedElement {
element_type: "input".to_string(),
content: "Username field".to_string(),
position: (50, 100),
size: (200, 30),
},
DetectedElement {
element_type: "input".to_string(),
content: "Password field".to_string(),
position: (50, 150),
size: (200, 30),
},
DetectedElement {
element_type: "button".to_string(),
content: "Login button".to_string(),
position: (50, 200),
size: (80, 30),
},
];
let spec = DesignSpec {
content: format!("Whiteboard sketch interpreted from: {}", image_path),
image_type: ImageType::Whiteboard,
detected_elements,
};
Ok(spec)
}
async fn interpret_screenshot(&self, image_path: &str) -> Result<DesignSpec> {
let detected_elements = vec![
DetectedElement {
element_type: "window".to_string(),
content: "Application window".to_string(),
position: (0, 0),
size: (800, 600),
},
DetectedElement {
element_type: "toolbar".to_string(),
content: "Menu bar".to_string(),
position: (0, 0),
size: (800, 30),
},
];
let spec = DesignSpec {
content: format!("Screenshot interpreted from: {}", image_path),
image_type: ImageType::Screenshot,
detected_elements,
};
Ok(spec)
}
async fn generate_code_from_visual_elements(&self, image_path: &str) -> Result<DesignSpec> {
let detected_elements = vec![DetectedElement {
element_type: "visual_element".to_string(),
content: "Visual element detected".to_string(),
position: (0, 0),
size: (100, 100),
}];
let spec = DesignSpec {
content: format!("Visual elements detected in: {}", image_path),
image_type: ImageType::Unknown,
detected_elements,
};
Ok(spec)
}
fn has_boxes_and_arrows(&self, _image: &DynamicImage) -> Result<bool> {
Ok(false)
}
fn has_ui_elements(&self, _image: &DynamicImage) -> Result<bool> {
Ok(false)
}
fn has_handwriting(&self, _image: &DynamicImage) -> Result<bool> {
Ok(false)
}
}
pub struct ImageProcessor;
impl ImageProcessor {
pub fn new() -> Self {
Self
}
pub fn load_image(&self, image_path: &str) -> Result<DynamicImage> {
let path = Path::new(image_path);
if !path.exists() {
return Err(anyhow::anyhow!("Image file does not exist: {}", image_path));
}
let img = image::open(path)?;
Ok(img)
}
pub fn resize_image(
&self,
img: &DynamicImage,
max_width: u32,
max_height: u32,
) -> DynamicImage {
let (width, height) = img.dimensions();
if width <= max_width && height <= max_height {
return img.clone();
}
let scale_factor = (max_width as f32 / width as f32).min(max_height as f32 / height as f32);
let new_width = (width as f32 * scale_factor) as u32;
let new_height = (height as f32 * scale_factor) as u32;
img.resize(new_width, new_height, image::imageops::Triangle)
}
pub fn detect_edges(&self, img: &DynamicImage) -> Result<DynamicImage> {
let mut grayscale_img = img.grayscale();
image::imageops::invert(&mut grayscale_img);
Ok(grayscale_img)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_image_processor_creation() {
let processor = ImageProcessor::new();
assert!(true); }
#[test]
fn test_design_spec_creation() {
let spec = DesignSpec {
content: "Test spec".to_string(),
image_type: ImageType::ArchitectureDiagram,
detected_elements: vec![],
};
assert_eq!(spec.content, "Test spec");
assert_eq!(spec.image_type, ImageType::ArchitectureDiagram);
assert!(spec.detected_elements.is_empty());
}
}