summaryrefslogtreecommitdiff
path: root/src/dev/new.rs
blob: 82aba42bb88d2612a0eeed848b98b153ecfdf053 (plain)
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
use anyhow::{bail, Context, Result};
use std::{
    env::set_current_dir,
    fs::{self, create_dir},
    path::Path,
    process::Command,
};

use crate::CURRENT_FORMAT_VERSION;

fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> {
    create_dir(dir_name)
        .with_context(|| format!("Failed to create the directory {current_dir}/{dir_name}"))?;
    println!("Created the directory {current_dir}/{dir_name}");
    Ok(())
}

fn write_rel_file<C>(file_name: &str, current_dir: &str, content: C) -> Result<()>
where
    C: AsRef<[u8]>,
{
    fs::write(file_name, content)
        .with_context(|| format!("Failed to create the file {current_dir}/{file_name}"))?;
    // Space to align with `create_rel_dir`.
    println!("Created the file      {current_dir}/{file_name}");
    Ok(())
}

pub fn new(path: &Path, no_git: bool) -> Result<()> {
    let dir_name = path.to_string_lossy();

    create_dir(path).with_context(|| format!("Failed to create the directory {dir_name}"))?;
    println!("Created the directory {dir_name}");

    set_current_dir(path)
        .with_context(|| format!("Failed to set {dir_name} as the current directory"))?;

    if !no_git
        && !Command::new("git")
            .arg("init")
            .status()
            .context("Failed to run `git init`")?
            .success()
    {
        bail!("`git init` didn't run successfully. See the error message above");
    }

    write_rel_file(".gitignore", &dir_name, crate::init::GITIGNORE)?;

    create_rel_dir("exercises", &dir_name)?;
    create_rel_dir("solutions", &dir_name)?;

    write_rel_file(
        "info.toml",
        &dir_name,
        format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"),
    )?;

    write_rel_file("Cargo.toml", &dir_name, CARGO_TOML)?;

    write_rel_file("README.md", &dir_name, README)?;

    create_rel_dir(".vscode", &dir_name)?;
    write_rel_file(
        ".vscode/extensions.json",
        &dir_name,
        crate::init::VS_CODE_EXTENSIONS_JSON,
    )?;

    println!("\nInitialization done ✓");

    Ok(())
}

const INFO_FILE_BEFORE_FORMAT_VERSION: &str =
    "# The format version is an indicator of the compatibility of third-party exercises with the
# Rustlings program.
# The format version is not the same as the version of the Rustlings program.
# In case Rustlings makes an unavoidable breaking change to the expected format of third-party
# exercises, you would need to raise this version and adapt to the new format.
# Otherwise, the newest version of the Rustlings program won't be able to run these exercises.
format_version = ";

const INFO_FILE_AFTER_FORMAT_VERSION: &str = r#"

# Optional multi-line message to be shown to users when just starting with the exercises.
welcome_message = """Welcome to these third-party Rustlings exercises."""

# Optional multi-line message to be shown to users after finishing all exercises.
final_message = """We hope that you found the exercises helpful :D"""

# Repeat this section for every exercise.
[[exercises]]
# Exercise name which is the exercise file name without the `.rs` extension.
name = "???"

# Optional directory name to be provided if you want to organize exercises in directories.
# If `dir` is specified, the exercise path is `exercises/DIR/NAME.rs`
# Otherwise, the path is `exercises/NAME.rs`
# dir = "???"

# The mode to run the exercise in.
# The mode "test" (preferred) runs the exercise's tests.
# The mode "run" only checks if the exercise compiles and runs it.
mode = "test"

# A multi-line hint to be shown to users on request.
hint = """???"""
"#;

const CARGO_TOML: &[u8] =
    br#"# Don't edit the `bin` list manually! It is updated by `rustlings dev update`
bin = []

[package]
name = "rustlings"
edition = "2021"
publish = false

[dependencies]
"#;

const README: &str = "# Rustlings 🦀

Welcome to these third-party Rustlings exercises 😃

First,
[install Rustlings using the official instructions in the README of the Rustlings project](https://github.com/rust-lang/rustlings) ✅

Then, open your terminal in this directory and run `rustlings` to get started with the exercises 🚀
";