pmat/
lib.rs

1pub mod cli;
2pub mod demo;
3pub mod handlers;
4#[cfg(feature = "pmcp-mcp")]
5pub mod mcp_pmcp;
6pub mod mcp_server;
7pub mod models;
8pub mod services;
9pub mod stateless_server;
10// #[cfg(test)]
11// pub mod testing;
12pub mod unified_protocol;
13pub mod utils;
14
15use anyhow::Result;
16use lru::LruCache;
17use std::num::NonZeroUsize;
18use std::sync::Arc;
19use tokio::sync::RwLock;
20use tracing::info;
21
22use crate::models::template::TemplateResource;
23use crate::services::renderer::TemplateRenderer;
24
25// Type aliases to reduce complexity
26type MetadataCache = Arc<RwLock<LruCache<String, Arc<TemplateResource>>>>;
27type ContentCache = Arc<RwLock<LruCache<String, Arc<str>>>>;
28
29// Dummy type for S3Client to satisfy trait requirements without AWS SDK
30pub struct S3Client;
31
32#[async_trait::async_trait]
33pub trait TemplateServerTrait: Send + Sync {
34    async fn get_template_metadata(&self, uri: &str) -> Result<Arc<TemplateResource>>;
35    async fn get_template_content(&self, s3_key: &str) -> Result<Arc<str>>;
36    async fn list_templates(&self, prefix: &str) -> Result<Vec<Arc<TemplateResource>>>;
37    fn get_renderer(&self) -> &TemplateRenderer;
38    fn get_metadata_cache(&self) -> Option<&MetadataCache>;
39    fn get_content_cache(&self) -> Option<&ContentCache>;
40    fn get_s3_client(&self) -> Option<&S3Client>;
41    fn get_bucket_name(&self) -> Option<&str>;
42}
43
44pub struct TemplateServer {
45    pub s3_client: S3Client,
46    pub bucket_name: String,
47    pub metadata_cache: MetadataCache,
48    pub content_cache: ContentCache,
49    pub renderer: TemplateRenderer,
50}
51
52impl TemplateServer {
53    pub async fn new() -> Result<Self> {
54        // Dummy implementation for Lambda compatibility
55        // The stateless server should be used instead
56        let cache_size = 1024;
57
58        Ok(Self {
59            s3_client: S3Client,
60            bucket_name: "dummy".to_string(),
61            metadata_cache: Arc::new(RwLock::new(LruCache::new(
62                NonZeroUsize::new(cache_size / 2).unwrap(),
63            ))),
64            content_cache: Arc::new(RwLock::new(LruCache::new(
65                NonZeroUsize::new(cache_size).unwrap(),
66            ))),
67            renderer: TemplateRenderer::new()?,
68        })
69    }
70
71    pub async fn warm_cache(&self) -> Result<()> {
72        let common_templates = vec![
73            "template://makefile/rust/cli",
74            "template://makefile/deno/cli",
75            "template://makefile/python-uv/cli",
76            "template://readme/rust/cli",
77            "template://gitignore/rust/cli",
78        ];
79
80        info!(
81            "Warming cache with {} common templates",
82            common_templates.len()
83        );
84
85        for template_uri in common_templates {
86            match self.get_template_metadata(template_uri).await {
87                Ok(resource) => {
88                    let _ = self.get_template_content(&resource.s3_object_key).await;
89                }
90                Err(e) => {
91                    info!("Failed to warm cache for {}: {}", template_uri, e);
92                }
93            }
94        }
95
96        Ok(())
97    }
98
99    pub async fn get_template_metadata(&self, _uri: &str) -> Result<Arc<TemplateResource>> {
100        // Dummy implementation - use StatelessTemplateServer instead
101        Err(anyhow::anyhow!(
102            "TemplateServer with S3 is deprecated. Use StatelessTemplateServer instead."
103        ))
104    }
105
106    pub async fn get_template_content(&self, _s3_key: &str) -> Result<Arc<str>> {
107        // Dummy implementation - use StatelessTemplateServer instead
108        Err(anyhow::anyhow!(
109            "TemplateServer with S3 is deprecated. Use StatelessTemplateServer instead."
110        ))
111    }
112}
113
114#[async_trait::async_trait]
115impl TemplateServerTrait for TemplateServer {
116    async fn get_template_metadata(&self, uri: &str) -> Result<Arc<TemplateResource>> {
117        self.get_template_metadata(uri).await
118    }
119
120    async fn get_template_content(&self, s3_key: &str) -> Result<Arc<str>> {
121        self.get_template_content(s3_key).await
122    }
123
124    async fn list_templates(&self, _prefix: &str) -> Result<Vec<Arc<TemplateResource>>> {
125        // Dummy implementation - use StatelessTemplateServer instead
126        Err(anyhow::anyhow!(
127            "TemplateServer with S3 is deprecated. Use StatelessTemplateServer instead."
128        ))
129    }
130
131    fn get_renderer(&self) -> &TemplateRenderer {
132        &self.renderer
133    }
134
135    fn get_metadata_cache(&self) -> Option<&MetadataCache> {
136        Some(&self.metadata_cache)
137    }
138
139    fn get_content_cache(&self) -> Option<&ContentCache> {
140        Some(&self.content_cache)
141    }
142
143    fn get_s3_client(&self) -> Option<&S3Client> {
144        Some(&self.s3_client)
145    }
146
147    fn get_bucket_name(&self) -> Option<&str> {
148        Some(&self.bucket_name)
149    }
150}
151
152// Public exports for CLI consumption
153pub use models::error::TemplateError;
154pub use models::template::{ParameterSpec, ParameterType};
155pub use services::template_service::{
156    generate_template, list_templates, scaffold_project, search_templates, validate_template,
157};
158
159// MCP server runner function
160pub async fn run_mcp_server<T: TemplateServerTrait + 'static>(server: Arc<T>) -> Result<()> {
161    use crate::models::mcp::{McpRequest, McpResponse};
162    use std::io::{self, BufRead, Write};
163    use tracing::{error, info};
164
165    info!("MCP server ready, waiting for requests on stdin...");
166
167    // Read JSON-RPC requests from stdin
168    let stdin = io::stdin();
169    let mut stdout = io::stdout();
170
171    for line in stdin.lock().lines() {
172        let line = line?;
173
174        // Skip empty lines
175        if line.trim().is_empty() {
176            continue;
177        }
178
179        // Parse the JSON-RPC request
180        match serde_json::from_str::<McpRequest>(&line) {
181            Ok(request) => {
182                info!(
183                    "Received request: method={}, id={:?}",
184                    request.method, request.id
185                );
186
187                // Handle the request using the existing handler
188                let response = handlers::handle_request(Arc::clone(&server), request).await;
189
190                // Write response to stdout
191                let response_json = serde_json::to_string(&response)?;
192                writeln!(stdout, "{response_json}")?;
193                stdout.flush()?;
194            }
195            Err(e) => {
196                error!("Failed to parse JSON-RPC request: {}", e);
197
198                // Send error response
199                let error_response = McpResponse::error(
200                    serde_json::Value::Null,
201                    -32700,
202                    format!("Parse error: {e}"),
203                );
204
205                let response_json = serde_json::to_string(&error_response)?;
206                writeln!(stdout, "{response_json}")?;
207                stdout.flush()?;
208            }
209        }
210    }
211
212    Ok(())
213}
214
215#[cfg(test)]
216mod tests {
217
218    #[path = "../tests/template_rendering.rs"]
219    mod template_rendering;
220
221    #[path = "../tests/claude_code_e2e.rs"]
222    mod claude_code_e2e;
223
224    #[path = "../tests/mcp_protocol.rs"]
225    mod mcp_protocol;
226
227    #[path = "../tests/template_resources.rs"]
228    mod template_resources;
229
230    #[path = "../tests/helpers.rs"]
231    mod helpers;
232
233    #[path = "../tests/quality_checks_property_tests.rs"]
234    mod quality_checks_property_tests;
235
236    #[path = "../tests/prompts.rs"]
237    mod prompts;
238
239    #[path = "../tests/binary_size.rs"]
240    mod binary_size;
241
242    #[path = "../tests/error.rs"]
243    mod error;
244
245    #[path = "../tests/cache.rs"]
246    mod cache;
247
248    #[path = "../tests/tools.rs"]
249    mod tools;
250
251    #[path = "../tests/analyze_cli_tests.rs"]
252    mod analyze_cli_tests;
253
254    #[path = "../tests/cli_integration_full.rs"]
255    mod cli_integration_full;
256
257    #[path = "../tests/resources.rs"]
258    mod resources;
259
260    #[path = "../tests/lib.rs"]
261    mod lib_tests;
262
263    #[path = "../tests/build_naming_validation.rs"]
264    mod build_naming_validation;
265
266    #[path = "../tests/ast_e2e.rs"]
267    mod ast_e2e;
268
269    #[path = "../tests/cli_tests.rs"]
270    mod cli_tests;
271
272    #[path = "../tests/churn.rs"]
273    mod churn;
274
275    #[path = "../tests/cli_simple_tests.rs"]
276    mod cli_simple_tests;
277
278    #[path = "../tests/deep_context_simplified_tests.rs"]
279    mod deep_context_simplified_tests;
280
281    #[path = "../tests/demo_comprehensive_tests.rs"]
282    mod demo_comprehensive_tests;
283
284    #[path = "../tests/http_adapter_tests.rs"]
285    mod http_adapter_tests;
286
287    #[path = "../tests/cache_comprehensive_tests.rs"]
288    mod cache_comprehensive_tests;
289
290    #[path = "../tests/unified_protocol_tests.rs"]
291    mod unified_protocol_tests;
292
293    #[path = "../tests/models.rs"]
294    mod models_tests;
295
296    #[path = "../tests/e2e_full_coverage.rs"]
297    mod e2e_full_coverage;
298
299    #[path = "../tests/additional_coverage.rs"]
300    mod additional_coverage;
301
302    #[path = "../tests/error_handling.rs"]
303    mod error_handling;
304
305    #[path = "../tests/cli_comprehensive_tests.rs"]
306    mod cli_comprehensive_tests;
307
308    #[path = "../tests/cli_property_tests.rs"]
309    mod cli_property_tests;
310
311    #[path = "../tests/ast_regression_test.rs"]
312    mod ast_regression_test;
313
314    #[path = "../tests/dead_code_verification.rs"]
315    mod dead_code_verification;
316
317    #[path = "../tests/complexity_distribution_verification.rs"]
318    mod complexity_distribution_verification;
319}