Skip to main content

allframe_forge/templates/
basic.rs

1//! Template strings for project scaffolding
2//!
3//! This module contains all the template strings used when generating
4//! new AllFrame projects with the `allframe ignite` command.
5//!
6//! All templates follow Clean Architecture principles and include:
7//! - Example code demonstrating proper layer separation
8//! - Documentation comments explaining each layer's purpose
9//! - Trait-based abstractions for dependency inversion
10
11/// Generate Cargo.toml content for a new AllFrame project
12///
13/// Creates a minimal `Cargo.toml` with:
14/// - Project name and metadata
15/// - Core dependencies: tokio, serde, anyhow, async-trait
16/// - Binary configuration
17///
18/// Note: The `allframe` crate dependency will be added once it's published to
19/// crates.io.
20///
21/// # Arguments
22/// * `project_name` - Name of the project (used for package name and binary
23///   name)
24///
25/// # Returns
26/// A formatted Cargo.toml file content as a String
27pub fn cargo_toml(project_name: &str) -> String {
28    // For now, we generate a minimal Cargo.toml without allframe dependency
29    // since allframe is not published to crates.io yet.
30    // When allframe is published, we'll add the features and allframe dependency.
31    format!(
32        r#"[package]
33name = "{}"
34version = "0.1.0"
35edition = "2021"
36rust-version = "1.89"
37
38[dependencies]
39tokio = {{ version = "1", features = ["full"] }}
40serde = {{ version = "1", features = ["derive"] }}
41anyhow = "1.0"
42async-trait = "0.1"
43
44[[bin]]
45name = "{}"
46path = "src/main.rs"
47"#,
48        project_name, project_name
49    )
50}
51
52/// Generate src/main.rs content with tokio runtime
53///
54/// Creates the application entry point with:
55/// - Module declarations for all Clean Architecture layers
56/// - Async main function with tokio runtime
57/// - Working example demonstrating layer connections
58///
59/// # Returns
60/// A static string containing the main.rs template
61pub fn main_rs() -> &'static str {
62    r#"//! AllFrame Application
63//!
64//! This is an AllFrame application following Clean Architecture principles.
65
66mod application;
67mod domain;
68mod infrastructure;
69mod presentation;
70
71use application::GreetingService;
72use infrastructure::ConsoleGreeter;
73
74#[tokio::main]
75async fn main() {
76    println!("AllFrame - One frame. Infinite transformations.");
77    println!();
78
79    // Wire up dependencies (Dependency Injection)
80    let greeter = ConsoleGreeter;
81    let service = GreetingService::new(greeter);
82
83    // Execute use case
84    service.greet("World").await;
85
86    println!();
87    println!("Your application is ready!");
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use domain::Greeter;
94
95    struct MockGreeter {
96        messages: std::cell::RefCell<Vec<String>>,
97    }
98
99    impl MockGreeter {
100        fn new() -> Self {
101            Self {
102                messages: std::cell::RefCell::new(Vec::new()),
103            }
104        }
105
106        fn get_messages(&self) -> Vec<String> {
107            self.messages.borrow().clone()
108        }
109    }
110
111    impl Greeter for MockGreeter {
112        fn greet(&self, name: &str) {
113            self.messages.borrow_mut().push(format!("Hello, {}!", name));
114        }
115    }
116
117    #[tokio::test]
118    async fn test_greeting_service() {
119        let greeter = MockGreeter::new();
120        let service = GreetingService::new(greeter);
121
122        service.greet("Test").await;
123
124        // Verify the greeter was called correctly
125        // In a real test, you'd check the mock's recorded calls
126    }
127
128    #[test]
129    fn test_mock_greeter() {
130        let greeter = MockGreeter::new();
131        greeter.greet("Alice");
132        greeter.greet("Bob");
133
134        let messages = greeter.get_messages();
135        assert_eq!(messages.len(), 2);
136        assert_eq!(messages[0], "Hello, Alice!");
137        assert_eq!(messages[1], "Hello, Bob!");
138    }
139}
140"#
141}
142
143/// Generate domain/mod.rs content
144pub fn domain_mod() -> &'static str {
145    r#"//! Domain Layer
146//!
147//! This layer contains the core business logic and has NO external dependencies.
148//!
149//! Structure:
150//! - entities/: Business entities with behavior
151//! - repositories/: Repository trait definitions (interfaces)
152//! - services/: Domain services for complex business logic
153//! - value_objects/: Immutable value types
154
155mod greeter;
156
157pub use greeter::Greeter;
158"#
159}
160
161/// Generate domain/greeter.rs content (replaces entities.rs)
162pub fn domain_greeter() -> &'static str {
163    r#"//! Greeter Domain Trait
164//!
165//! This trait defines the contract for greeting functionality.
166//! Implementations live in the infrastructure layer.
167
168/// A trait for greeting functionality.
169///
170/// This is a domain-level abstraction that allows different
171/// implementations (console, HTTP, mock for testing, etc.)
172pub trait Greeter {
173    /// Greet someone by name.
174    fn greet(&self, name: &str);
175}
176"#
177}
178
179/// Generate application/mod.rs content
180pub fn application_mod() -> &'static str {
181    r#"//! Application Layer
182//!
183//! This layer orchestrates domain objects to fulfill use cases.
184//! It depends only on the domain layer.
185
186mod greeting_service;
187
188pub use greeting_service::GreetingService;
189"#
190}
191
192/// Generate application/greeting_service.rs content
193pub fn application_greeting_service() -> &'static str {
194    r#"//! Greeting Service
195//!
196//! Application service that orchestrates the greeting use case.
197
198use crate::domain::Greeter;
199
200/// Service for greeting users.
201///
202/// This service demonstrates dependency injection - it accepts
203/// any implementation of the Greeter trait.
204pub struct GreetingService<G: Greeter> {
205    greeter: G,
206}
207
208impl<G: Greeter> GreetingService<G> {
209    /// Create a new GreetingService with the given greeter implementation.
210    pub fn new(greeter: G) -> Self {
211        Self { greeter }
212    }
213
214    /// Greet someone by name.
215    pub async fn greet(&self, name: &str) {
216        self.greeter.greet(name);
217    }
218}
219"#
220}
221
222/// Generate infrastructure/mod.rs content
223pub fn infrastructure_mod() -> &'static str {
224    r#"//! Infrastructure Layer
225//!
226//! This layer implements domain traits and handles external concerns.
227//! It depends on the domain layer (implements traits defined there).
228
229mod console_greeter;
230
231pub use console_greeter::ConsoleGreeter;
232"#
233}
234
235/// Generate infrastructure/console_greeter.rs content
236pub fn infrastructure_console_greeter() -> &'static str {
237    r#"//! Console Greeter Implementation
238//!
239//! A concrete implementation of the Greeter trait that prints to the console.
240
241use crate::domain::Greeter;
242
243/// A greeter that prints greetings to the console.
244pub struct ConsoleGreeter;
245
246impl Greeter for ConsoleGreeter {
247    fn greet(&self, name: &str) {
248        println!("Hello, {}!", name);
249    }
250}
251"#
252}
253
254/// Generate presentation/mod.rs content
255pub fn presentation_mod() -> &'static str {
256    r#"//! Presentation Layer
257//!
258//! This layer handles HTTP/gRPC/GraphQL requests and responses.
259//! It depends on the application and domain layers.
260//!
261//! Add your HTTP handlers, GraphQL resolvers, or gRPC services here.
262//! Example:
263//!
264//! ```ignore
265//! mod handlers;
266//! pub use handlers::*;
267//! ```
268"#
269}
270
271/// Generate .gitignore content
272pub fn gitignore() -> &'static str {
273    r#"# Rust
274target/
275Cargo.lock
276
277# IDE
278.vscode/
279.idea/
280*.swp
281*.swo
282
283# OS
284.DS_Store
285Thumbs.db
286
287# Environment
288.env
289.env.local
290
291# Logs
292*.log
293"#
294}
295
296/// Generate README.md content for the project
297///
298/// Creates comprehensive project documentation including:
299/// - Quick start guide
300/// - Project structure explanation
301/// - Feature highlights
302///
303/// # Arguments
304/// * `project_name` - Name of the project (used in title)
305///
306/// # Returns
307/// A formatted README.md file content as a String
308pub fn readme(project_name: &str) -> String {
309    format!(
310        r#"# {}
311
312An AllFrame application following Clean Architecture principles.
313
314## Quick Start
315
316```bash
317# Run the application
318cargo run
319
320# Run tests
321cargo test
322
323# Run with all features
324cargo run --all-features
325```
326
327## Structure
328
329This project follows Clean Architecture:
330
331```
332src/
333├── domain/          # Core business logic (no external dependencies)
334├── application/     # Use case orchestration
335├── infrastructure/  # External implementations (database, HTTP clients)
336└── presentation/    # HTTP/gRPC/GraphQL handlers
337```
338
339## Features
340
341- Clean Architecture enforced
342- TDD-first development
343- Protocol-agnostic (REST/GraphQL/gRPC)
344
345## Built with AllFrame
346
347[AllFrame](https://github.com/all-source-os/all-frame) - The composable Rust API framework.
348
349*One frame. Infinite transformations.*
350"#,
351        project_name
352    )
353}