Skip to main content

nargo_server/
lib.rs

1//! Nargo development server.
2//!
3//! This module provides development server, static file serving, and mock server functionality.
4
5#![warn(missing_docs)]
6
7use nargo_types::Result;
8use std::{
9    collections::HashMap,
10    path::{Path, PathBuf},
11    sync::Arc,
12};
13
14use nargo_types::NargoContext;
15use serde::{Deserialize, Serialize};
16
17/// Server mode.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ServerMode {
20    /// Development mode with hot reload.
21    Dev,
22    /// Static file serving mode.
23    Serve,
24    /// Production mode.
25    Prod,
26}
27
28/// Server options.
29#[derive(Debug, Clone)]
30pub struct ServerOptions {
31    /// Server mode.
32    pub mode: ServerMode,
33    /// Root directory.
34    pub root: PathBuf,
35    /// Port to listen on.
36    pub port: u16,
37    /// Host to bind to.
38    pub host: String,
39}
40
41impl Default for ServerOptions {
42    fn default() -> Self {
43        Self { mode: ServerMode::Dev, root: PathBuf::from("."), port: 3000, host: "127.0.0.1".to_string() }
44    }
45}
46
47/// Development server.
48pub struct DevServer {
49    options: ServerOptions,
50}
51
52impl DevServer {
53    /// Create a new dev server.
54    pub fn new(options: ServerOptions) -> Self {
55        Self { options }
56    }
57
58    /// Start the server.
59    pub async fn start(&self) -> Result<()> {
60        println!("Dev server running on {}:{}...", self.options.host, self.options.port);
61        Ok(())
62    }
63}
64
65/// Run the server with the given options.
66pub async fn run(_ctx: Arc<NargoContext>, options: ServerOptions) -> Result<()> {
67    let server = DevServer::new(options);
68    server.start().await
69}
70
71/// Mock route definition.
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct MockRoute {
74    /// Route path pattern.
75    pub path: String,
76    /// HTTP method (GET, POST, etc.).
77    #[serde(default = "default_method")]
78    pub method: String,
79    /// Response status code.
80    #[serde(default = "default_status")]
81    pub status: u16,
82    /// Response headers.
83    #[serde(default)]
84    pub headers: HashMap<String, String>,
85    /// Response body.
86    #[serde(default)]
87    pub body: String,
88    /// Response body file path (alternative to body).
89    #[serde(default)]
90    pub body_file: Option<PathBuf>,
91    /// Delay in milliseconds before responding.
92    #[serde(default)]
93    pub delay_ms: u64,
94}
95
96fn default_method() -> String {
97    "GET".to_string()
98}
99
100fn default_status() -> u16 {
101    200
102}
103
104/// Mock server for development.
105pub struct MockServer {
106    _ctx: Arc<NargoContext>,
107    routes: Vec<MockRoute>,
108}
109
110impl MockServer {
111    /// Create a new mock server.
112    pub fn new(ctx: Arc<NargoContext>) -> Self {
113        Self { _ctx: ctx, routes: Vec::new() }
114    }
115
116    /// Add a route to the mock server.
117    pub fn add_route(&mut self, route: MockRoute) {
118        self.routes.push(route);
119    }
120
121    /// Load routes from a directory.
122    pub fn load_from_dir(&mut self, dir: &Path) -> Result<()> {
123        for entry in std::fs::read_dir(dir).map_err(|e| nargo_types::Error::external_error("server".to_string(), e.to_string(), nargo_types::Span::unknown()))? {
124            let entry = entry.map_err(|e| nargo_types::Error::external_error("server".to_string(), e.to_string(), nargo_types::Span::unknown()))?;
125            let path = entry.path();
126            if path.extension().map_or(false, |ext| ext == "json") {
127                let content = std::fs::read_to_string(&path).map_err(|e| nargo_types::Error::external_error("server".to_string(), e.to_string(), nargo_types::Span::unknown()))?;
128                let routes: Vec<MockRoute> = serde_json::from_str(&content).map_err(|e| nargo_types::Error::external_error("server".to_string(), e.to_string(), nargo_types::Span::unknown()))?;
129                self.routes.extend(routes);
130            }
131        }
132        Ok(())
133    }
134
135    /// Get the number of routes.
136    pub fn routes_count(&self) -> usize {
137        self.routes.len()
138    }
139
140    /// Start the mock server.
141    pub async fn start(&self, port: u16) -> Result<()> {
142        println!("Mock server running on port {}...", port);
143        println!("Loaded {} routes", self.routes.len());
144        Ok(())
145    }
146}