ralph/commands/tutorial/
sandbox.rs1use anyhow::{Context, Result};
9use std::path::PathBuf;
10use tempfile::TempDir;
11
12const SAMPLE_CARGO_TOML: &str = r#"[package]
14name = "tutorial-project"
15version = "0.1.0"
16edition = "2021"
17
18[dependencies]
19"#;
20
21const SAMPLE_LIB_RS: &str = r#"//! Tutorial project for Ralph onboarding.
22//!
23//! Add your code here.
24
25/// Returns a greeting message.
26pub fn greet(name: &str) -> String {
27 format!("Hello, {}!", name)
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33
34 #[test]
35 fn test_greet() {
36 assert_eq!(greet("World"), "Hello, World!");
37 }
38}
39"#;
40
41pub struct TutorialSandbox {
43 temp_dir: Option<TempDir>,
45 pub path: PathBuf,
47}
48
49impl TutorialSandbox {
50 pub fn create() -> Result<Self> {
52 let temp_dir =
53 TempDir::new().context("failed to create temp directory for tutorial sandbox")?;
54 let path = temp_dir.path().to_path_buf();
55
56 let status = std::process::Command::new("git")
58 .current_dir(&path)
59 .args(["init", "--quiet"])
60 .status()
61 .context("run git init")?;
62 anyhow::ensure!(status.success(), "git init failed");
63
64 std::process::Command::new("git")
66 .current_dir(&path)
67 .args(["config", "user.name", "Ralph Tutorial"])
68 .status()
69 .context("set git user.name")?;
70 std::process::Command::new("git")
71 .current_dir(&path)
72 .args(["config", "user.email", "tutorial@ralph.invalid"])
73 .status()
74 .context("set git user.email")?;
75
76 std::fs::write(path.join("Cargo.toml"), SAMPLE_CARGO_TOML)?;
78 std::fs::create_dir_all(path.join("src"))?;
79 std::fs::write(path.join("src/lib.rs"), SAMPLE_LIB_RS)?;
80
81 std::fs::write(
83 path.join(".gitignore"),
84 "/target\n.ralph/lock\n.ralph/cache/\n.ralph/logs/\n",
85 )?;
86
87 std::process::Command::new("git")
89 .current_dir(&path)
90 .args(["add", "."])
91 .status()
92 .context("git add")?;
93 std::process::Command::new("git")
94 .current_dir(&path)
95 .args(["commit", "--quiet", "-m", "Initial commit"])
96 .status()
97 .context("git commit")?;
98
99 Ok(Self {
100 temp_dir: Some(temp_dir),
101 path,
102 })
103 }
104
105 pub fn preserve(mut self) -> PathBuf {
107 let path = self.path.clone();
108 if let Some(temp_dir) = self.temp_dir.take() {
110 let _ = temp_dir.keep();
112 }
113 path
114 }
115}
116
117impl Drop for TutorialSandbox {
118 fn drop(&mut self) {
119 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn sandbox_creates_files() {
129 let sandbox = TutorialSandbox::create().unwrap();
130
131 assert!(sandbox.path.join("Cargo.toml").exists());
132 assert!(sandbox.path.join("src/lib.rs").exists());
133 assert!(sandbox.path.join(".gitignore").exists());
134 assert!(sandbox.path.join(".git").exists());
135 }
136
137 #[test]
138 fn sandbox_preserve_prevents_cleanup() {
139 let sandbox = TutorialSandbox::create().unwrap();
140 let path = sandbox.preserve();
141
142 assert!(path.exists());
144
145 let _ = std::fs::remove_dir_all(&path);
147 }
148}