use dx_forge::{DxTool, ExecutionContext, ToolOutput};
use anyhow::Result;
use std::path::PathBuf;
pub struct DxUiTool {
enabled: bool,
component_dir: PathBuf,
}
impl DxUiTool {
pub fn new(component_dir: PathBuf) -> Self {
Self {
enabled: true,
component_dir,
}
}
}
impl DxTool for DxUiTool {
fn name(&self) -> &str {
"dx-ui"
}
fn version(&self) -> &str {
"1.0.0"
}
fn priority(&self) -> u32 {
30 }
fn execute(&mut self, ctx: &ExecutionContext) -> Result<ToolOutput> {
println!("🎨 DX-UI: Processing UI components...");
let mut output = ToolOutput::success();
let button_path = self.component_dir.join("Button.tsx");
let button_code = r#"
import React from 'react';
export interface ButtonProps {
variant?: 'primary' | 'secondary';
children: React.ReactNode;
}
export const Button: React.FC<ButtonProps> = ({ variant = 'primary', children }) => {
return (
<button className={`dx-button dx-button-${variant}`}>
{children}
</button>
);
};
"#;
std::fs::create_dir_all(&self.component_dir)?;
std::fs::write(&button_path, button_code)?;
output.files_created.push(button_path);
output.message = "Generated UI components".to_string();
println!("✅ DX-UI: Created {} components", output.files_created.len());
Ok(output)
}
fn should_run(&self, ctx: &ExecutionContext) -> bool {
if !self.enabled {
return false;
}
ctx.changed_files.iter().any(|f| {
f.extension()
.and_then(|e| e.to_str())
.map_or(false, |e| e == "ts" || e == "tsx")
})
}
fn dependencies(&self) -> Vec<String> {
vec![] }
fn before_execute(&mut self, _ctx: &ExecutionContext) -> Result<()> {
if !self.component_dir.exists() {
println!("📁 Creating component directory: {:?}", self.component_dir);
}
Ok(())
}
fn after_execute(&mut self, _ctx: &ExecutionContext, output: &ToolOutput) -> Result<()> {
if output.success {
println!("📊 DX-UI Stats:");
println!(" - Created: {} files", output.files_created.len());
println!(" - Modified: {} files", output.files_modified.len());
}
Ok(())
}
fn on_error(&mut self, _ctx: &ExecutionContext, error: &anyhow::Error) -> Result<()> {
eprintln!("❌ DX-UI failed: {}", error);
Ok(())
}
}
pub struct DxCodegenTool {
schema_dir: PathBuf,
output_dir: PathBuf,
}
impl DxCodegenTool {
pub fn new(schema_dir: PathBuf, output_dir: PathBuf) -> Self {
Self {
schema_dir,
output_dir,
}
}
}
impl DxTool for DxCodegenTool {
fn name(&self) -> &str {
"dx-codegen"
}
fn version(&self) -> &str {
"1.0.0"
}
fn priority(&self) -> u32 {
10 }
fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
println!("⚙️ DX-Codegen: Generating code from schemas...");
let mut output = ToolOutput::success();
let types_path = self.output_dir.join("generated_types.ts");
let types_code = r#"
// Auto-generated by dx-codegen
// DO NOT EDIT MANUALLY
export interface User {
id: string;
name: string;
email: string;
}
export interface Product {
id: string;
title: string;
price: number;
}
"#;
std::fs::create_dir_all(&self.output_dir)?;
std::fs::write(&types_path, types_code)?;
output.files_created.push(types_path);
output.message = "Generated types from schemas".to_string();
println!("✅ DX-Codegen: Generated {} files", output.files_created.len());
Ok(output)
}
fn should_run(&self, ctx: &ExecutionContext) -> bool {
ctx.changed_files.iter().any(|f| {
f.extension()
.and_then(|e| e.to_str())
.map_or(false, |e| e == "schema" || e == "graphql")
})
}
fn dependencies(&self) -> Vec<String> {
vec![] }
}
pub struct DxStyleTool {
style_dir: PathBuf,
}
impl DxStyleTool {
pub fn new(style_dir: PathBuf) -> Self {
Self { style_dir }
}
}
impl DxTool for DxStyleTool {
fn name(&self) -> &str {
"dx-style"
}
fn version(&self) -> &str {
"1.0.0"
}
fn priority(&self) -> u32 {
40 }
fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
println!("🎨 DX-Style: Processing styles...");
let mut output = ToolOutput::success();
let css_path = self.style_dir.join("components.css");
let css_code = r#"
/* Auto-generated by dx-style */
:root {
--dx-color-primary: #007bff;
--dx-color-secondary: #6c757d;
--dx-spacing-sm: 0.5rem;
--dx-spacing-md: 1rem;
--dx-spacing-lg: 1.5rem;
}
.dx-button {
padding: var(--dx-spacing-sm) var(--dx-spacing-md);
border-radius: 4px;
font-weight: 500;
cursor: pointer;
}
.dx-button-primary {
background-color: var(--dx-color-primary);
color: white;
}
"#;
std::fs::create_dir_all(&self.style_dir)?;
std::fs::write(&css_path, css_code)?;
output.files_created.push(css_path);
output.message = "Processed styles".to_string();
println!("✅ DX-Style: Processed styles successfully");
Ok(output)
}
fn should_run(&self, ctx: &ExecutionContext) -> bool {
ctx.changed_files.iter().any(|f| {
f.extension().and_then(|e| e.to_str()).map_or(false, |e| {
e == "css" || e == "scss" || e == "sass" || e == "less"
})
})
}
fn dependencies(&self) -> Vec<String> {
vec!["dx-codegen".to_string()] }
}
pub struct DxOptimizerTool;
impl DxTool for DxOptimizerTool {
fn name(&self) -> &str {
"dx-optimizer"
}
fn version(&self) -> &str {
"1.0.0"
}
fn priority(&self) -> u32 {
90 }
fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
println!("⚡ DX-Optimizer: Optimizing output...");
let mut output = ToolOutput::success();
output.message = "Optimization complete".to_string();
Ok(output)
}
fn dependencies(&self) -> Vec<String> {
vec![
"dx-codegen".to_string(),
"dx-ui".to_string(),
"dx-style".to_string(),
]
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_dx_ui_tool_creation() {
let temp_dir = TempDir::new().unwrap();
let tool = DxUiTool::new(temp_dir.path().to_path_buf());
assert_eq!(tool.name(), "dx-ui");
assert_eq!(tool.priority(), 30);
}
#[test]
fn test_dx_codegen_execution() {
let temp_dir = TempDir::new().unwrap();
let schema_dir = temp_dir.path().join("schemas");
let output_dir = temp_dir.path().join("generated");
let mut tool = DxCodegenTool::new(schema_dir, output_dir.clone());
let ctx = ExecutionContext::new(
temp_dir.path().to_path_buf(),
temp_dir.path().join(".dx/forge"),
);
let result = tool.execute(&ctx).unwrap();
assert!(result.success);
assert_eq!(result.files_created.len(), 1);
assert!(output_dir.join("generated_types.ts").exists());
}
#[test]
fn test_tool_dependencies() {
let optimizer = DxOptimizerTool;
let deps = optimizer.dependencies();
assert_eq!(deps.len(), 3);
assert!(deps.contains(&"dx-codegen".to_string()));
assert!(deps.contains(&"dx-ui".to_string()));
assert!(deps.contains(&"dx-style".to_string()));
}
}