amberjs 1.16.1

JavaScript/TypeScript runtime built with Rust and V8
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
// Doctor Command Module
// Stage 91 Phase 4.1 - 环境诊断命令
//
/// 实现 `amberjs doctor` 命令,诊断开发环境问题
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use super::output_formatter::OutputFormatter;
use std::io::Write;
/// 诊断检查结果
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CheckStatus {
    /// 检查通过
    Pass,
    /// 警告
    Warning,
    /// 失败
    Fail,
    /// 跳过
    Skip,
}
impl CheckStatus {
    fn icon(&self) -> &'static str {
        match self {
            CheckStatus::Pass => "✓",
            CheckStatus::Warning => "⚠",
            CheckStatus::Fail => "✗",
            CheckStatus::Skip => "○",
        }
    }
    fn color(&self) -> &'static str {
        match self {
            CheckStatus::Pass => "\x1b[32m",    // Green
            CheckStatus::Warning => "\x1b[33m", // Yellow
            CheckStatus::Fail => "\x1b[31m",    // Red
            CheckStatus::Skip => "\x1b[90m",    // Gray
        }
    }
}
/// 单个诊断检查
#[derive(Debug)]
pub struct DiagnosticCheck {
    /// 检查名称
    pub name: String,
    /// 检查描述
    pub description: String,
    /// 检查状态
    pub status: CheckStatus,
    /// 详细消息
    pub message: Option<String>,
    /// 建议操作
    pub suggestion: Option<String>,
}
/// Doctor 命令执行器
pub struct DoctorCommand {
    formatter: OutputFormatter,
    verbose: bool,
    checks: Vec<DiagnosticCheck>,
}
impl DoctorCommand {
    /// 创建新的 doctor 命令
    pub fn new(verbose: bool) -> Self {
        Self {
            formatter: OutputFormatter::with_verbose(verbose),
            verbose,
            checks: Vec::new(),
        }
    }
    /// 执行所有诊断检查
    pub fn execute(&mut self) -> anyhow::Result<()> {
        let start: _ = Instant::now();
        self.formatter.title("Amber Environment Diagnostics");
        println!();
        // 运行所有检查
        self.check_amberjs_version();
        self.check_v8_engine();
        self.check_rust_toolchain();
        self.check_node_compatibility();
        self.check_git();
        self.check_package_managers();
        self.check_disk_space();
        self.check_network();
        self.check_permissions();
        // 打印结果
        self.print_results();
        // 统计
        let elapsed: _ = start.elapsed();
        let (pass, warn, fail) = self.count_status();
        println!();
        self.formatter.title("Summary");
        if self.formatter.color_enabled {
            println!(
                "  \x1b[32m{} passed\x1b[0m, \x1b[33m{} warnings\x1b[0m, \x1b[31m{} failed\x1b[0m",
                pass, warn, fail
            );
        } else {
            println!("  {} passed, {} warnings, {} failed", pass, warn, fail);
        }
        println!(
            "  Completed in {}",
            OutputFormatter::format_duration(elapsed)
        );
        println!();
        if fail > 0 {
            self.formatter
                .warning("Some checks failed. See suggestions above.");
        } else if warn > 0 {
            self.formatter
                .info("All critical checks passed. Some warnings to review.");
        } else {
            self.formatter
                .success("All checks passed! Your environment is ready.");
        }
        Ok(())
    }
    fn add_check(&mut self, check: DiagnosticCheck) {
        self.checks.push(check);
    }
    fn check_amberjs_version(&mut self) {
        let version: _ = env!("CARGO_PKG_VERSION");
        self.add_check(DiagnosticCheck {
            name: "Amber Version".to_string(),
            description: "Check Amber runtime version".to_string(),
            status: CheckStatus::Pass,
            message: Some(format!("v{}", version)),
            suggestion: None,
        });
    }
    fn check_v8_engine(&mut self) {
        // V8 引擎通常与 rusty_v8 绑定
        let v8_version: _ = "10.x (rusty_v8 0.22)";
        self.add_check(DiagnosticCheck {
            name: "V8 JavaScript Engine".to_string(),
            description: "Check V8 engine availability".to_string(),
            status: CheckStatus::Pass,
            message: Some(v8_version.to_string()),
            suggestion: None,
        });
    }
    fn check_rust_toolchain(&mut self) {
        match Command::new("rustc").arg("--version").output() {
            Ok(output) if output.status.success() => {
                let version: _ = String::from_utf8_lossy(&output.stdout);
                self.add_check(DiagnosticCheck {
                    name: "Rust Toolchain".to_string(),
                    description: "Check Rust compiler".to_string(),
                    status: CheckStatus::Pass,
                    message: Some(version.trim().to_string()),
                    suggestion: None,
                });
            }
            _ => {
                self.add_check(DiagnosticCheck {
                    name: "Rust Toolchain".to_string(),
                    description: "Check Rust compiler".to_string(),
                    status: CheckStatus::Warning,
                    message: Some("Not found (optional for development)".to_string()),
                    suggestion: Some("Install Rust from https://rustup.rs".to_string()),
                });
            }
        }
    }
    fn check_node_compatibility(&mut self) {
        // 检查是否有 Node.js (用于比较和兼容性)
        match Command::new("node").arg("--version").output() {
            Ok(output) if output.status.success() => {
                let version: _ = String::from_utf8_lossy(&output.stdout);
                self.add_check(DiagnosticCheck {
                    name: "Node.js (Compatibility)".to_string(),
                    description: "Check Node.js for comparison".to_string(),
                    status: CheckStatus::Pass,
                    message: Some(version.trim().to_string()),
                    suggestion: None,
                });
            }
            _ => {
                self.add_check(DiagnosticCheck {
                    name: "Node.js (Compatibility)".to_string(),
                    description: "Check Node.js for comparison".to_string(),
                    status: CheckStatus::Skip,
                    message: Some("Not installed (optional)".to_string()),
                    suggestion: None,
                });
            }
        }
    }
    fn check_git(&mut self) {
        match Command::new("git").arg("--version").output() {
            Ok(output) if output.status.success() => {
                let version: _ = String::from_utf8_lossy(&output.stdout);
                self.add_check(DiagnosticCheck {
                    name: "Git".to_string(),
                    description: "Version control system".to_string(),
                    status: CheckStatus::Pass,
                    message: Some(version.trim().to_string()),
                    suggestion: None,
                });
            }
            _ => {
                self.add_check(DiagnosticCheck {
                    name: "Git".to_string(),
                    description: "Version control system".to_string(),
                    status: CheckStatus::Warning,
                    message: Some("Not installed".to_string()),
                    suggestion: Some("Install Git for version control".to_string()),
                });
            }
        }
    }
    fn check_package_managers(&mut self) {
        // Check npm
        let npm_status: _ = match Command::new("npm").arg("--version").output() {
            Ok(output) if output.status.success() => {
                let version: _ = String::from_utf8_lossy(&output.stdout);
                (CheckStatus::Pass, Some(format!("npm {}", version.trim())))
            }
            _ => (CheckStatus::Skip, Some("npm not found".to_string())),
        };
        // Check yarn
        let yarn_status: _ = match Command::new("yarn").arg("--version").output() {
            Ok(output) if output.status.success() => {
                let version: _ = String::from_utf8_lossy(&output.stdout);
                (CheckStatus::Pass, Some(format!("yarn {}", version.trim())))
            }
            _ => (CheckStatus::Skip, None),
        };
        // Check pnpm
        let pnpm_status: _ = match Command::new("pnpm").arg("--version").output() {
            Ok(output) if output.status.success() => {
                let version: _ = String::from_utf8_lossy(&output.stdout);
                (CheckStatus::Pass, Some(format!("pnpm {}", version.trim())))
            }
            _ => (CheckStatus::Skip, None),
        };
        let mut managers = Vec::new();
        if npm_status.0 == CheckStatus::Pass {
            managers.push(npm_status.1.unwrap());
        }
        if yarn_status.0 == CheckStatus::Pass {
            managers.push(yarn_status.1.unwrap());
        }
        if pnpm_status.0 == CheckStatus::Pass {
            managers.push(pnpm_status.1.unwrap());
        }
        let (status, message) = if managers.is_empty() {
            (CheckStatus::Warning, "No package manager found".to_string())
        } else {
            (CheckStatus::Pass, managers.join(", "))
        };
        self.add_check(DiagnosticCheck {
            name: "Package Managers".to_string(),
            description: "npm/yarn/pnpm availability".to_string(),
            status,
            message: Some(message),
            suggestion: if status == CheckStatus::Warning {
                Some("Install npm, yarn, or pnpm for package management".to_string())
            } else {
                None
            },
        });
    }
    fn check_disk_space(&mut self) {
        // 获取当前目录的磁盘空间
        let cwd: _ = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
        #[cfg(unix)]
        {
            use std::ffi::CString;
            let path: _ = CString::new(cwd.to_string_lossy().as_bytes()).unwrap();
            let mut stat: libc::statvfs = unsafe { std::mem::zeroed() };
            let result: _ = unsafe { libc::statvfs(path.as_ptr(), &mut stat) };
            if result == 0 {
                let free_bytes: _ = stat.f_bavail as u64 * stat.f_bsize as u64;
                let free_gb: _ = free_bytes / (1024 * 1024 * 1024);
                let (status, suggestion) = if free_gb < 1 {
                    (
                        CheckStatus::Fail,
                        Some("Less than 1GB free. Free up disk space.".to_string()),
                    )
                } else if free_gb < 5 {
                    (
                        CheckStatus::Warning,
                        Some("Low disk space. Consider cleaning up.".to_string()),
                    )
                } else {
                    (CheckStatus::Pass, None)
                };
                self.add_check(DiagnosticCheck {
                    name: "Disk Space".to_string(),
                    description: "Available disk space".to_string(),
                    status,
                    message: Some(format!("{} GB free", free_gb)),
                    suggestion,
                });
                return;
            }
        }
        // Fallback for non-Unix or failed check
        self.add_check(DiagnosticCheck {
            name: "Disk Space".to_string(),
            description: "Available disk space".to_string(),
            status: CheckStatus::Skip,
            message: Some("Unable to determine".to_string()),
            suggestion: None,
        });
    }
    fn check_network(&mut self) {
        // 简单的网络连通性检查
        #[cfg(unix)]
        {
            let result: _ = Command::new("ping")
                .args(["-c", "1", "-W", "2", "8.8.8.8"])
                .output();
            let (status, message) = match result {
                Ok(output) if output.status.success() => (
                    CheckStatus::Pass,
                    "Internet connection available".to_string(),
                ),
                _ => (
                    CheckStatus::Warning,
                    "Internet connection unavailable or slow".to_string(),
                ),
            };
            self.add_check(DiagnosticCheck {
                name: "Network Connectivity".to_string(),
                description: "Internet connection check".to_string(),
                status,
                message: Some(message),
                suggestion: if status == CheckStatus::Warning {
                    Some("Check your internet connection".to_string())
                } else {
                    None
                },
            });
            return;
        }
        #[cfg(not(unix))]
        {
            self.add_check(DiagnosticCheck {
                name: "Network Connectivity".to_string(),
                description: "Internet connection check".to_string(),
                status: CheckStatus::Skip,
                message: Some("Skipped on this platform".to_string()),
                suggestion: None,
            });
        }
    }
    fn check_permissions(&mut self) {
        let cwd: _ = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
        // 检查写入权限
        let test_file: _ = cwd.join(".amberjs-permission-test");
        let can_write: _ = fs::write(&test_file, "test").is_ok();
        if can_write {
            let _: _ = fs::remove_file(&test_file);
        }
        let (status, message) = if can_write {
            (CheckStatus::Pass, "Write permissions OK".to_string())
        } else {
            (
                CheckStatus::Fail,
                "No write permissions in current directory".to_string(),
            )
        };
        self.add_check(DiagnosticCheck {
            name: "File Permissions".to_string(),
            description: "Write access to current directory".to_string(),
            status,
            message: Some(message),
            suggestion: if status == CheckStatus::Fail {
                Some("Check directory permissions or change to a writable directory".to_string())
            } else {
                None
            },
        });
    }
    fn print_results(&self) {
        for check in &self.checks {
            let icon: _ = check.status.icon();
            let color: _ = check.status.color();
            if self.formatter.color_enabled {
                print!("  {}{}\x1b[0m {}", color, icon, check.name);
            } else {
                print!("  {} {}", icon, check.name);
            }
            if let Some(msg) = &check.message {
                print!(" - {}", msg);
            }
            println!();
            // 打印建议 (仅在非 pass 状态)
            if let Some(suggestion) = &check.suggestion {
                if self.formatter.color_enabled {
                    println!("    \x1b[90m└─ {}\x1b[0m", suggestion);
                } else {
                    println!("    └─ {}", suggestion);
                }
            }
        }
    }
    fn count_status(&self) -> (usize, usize, usize) {
        let mut pass = 0;
        let mut warn = 0;
        let mut fail = 0;
        for check in &self.checks {
            match check.status {
                CheckStatus::Pass => pass += 1,
                CheckStatus::Warning => warn += 1,
                CheckStatus::Fail => fail += 1,
                CheckStatus::Skip => {}
            }
        }
        (pass, warn, fail)
    }
}
#[cfg(test)]
mod tests {
    use super::*;
use std::collections::{HashMap, BTreeMap};
use std::fs::File;
use anyhow::{Result, Error};
use std::time::Instant;
    #[test]
    fn test_check_status_icon() {
        assert_eq!(CheckStatus::Pass.icon(), "✓");
        assert_eq!(CheckStatus::Warning.icon(), "⚠");
        assert_eq!(CheckStatus::Fail.icon(), "✗");
        assert_eq!(CheckStatus::Skip.icon(), "○");
    }
    #[test]
    fn test_doctor_command_execute() {
        let mut cmd = DoctorCommand::new(false);
        assert!(cmd.execute().is_ok());
    }
    #[test]
    fn test_doctor_command_checks() {
        let mut cmd = DoctorCommand::new(true);
        cmd.check_amberjs_version();
        cmd.check_v8_engine();
        assert!(cmd.checks.len() >= 2);
        assert!(cmd.checks.iter().any(|c| c.name == "Amber Version"));
    }
}