Skip to content

Commit

Permalink
Merge pull request #2 from uptudev/dev
Browse files Browse the repository at this point in the history
Add arg parsing; working Rust solution with more to come
  • Loading branch information
uptudev authored Feb 19, 2024
2 parents e03369c + b9a6aea commit 4fb9764
Show file tree
Hide file tree
Showing 15 changed files with 398 additions and 56 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
debug/
target/

# The name I usually give to my testing project
temp/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[package]
name = "project_initializer"
name = "pi"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.5.1", features = ["derive"] }
colored = "2.1.0"
54 changes: 52 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,52 @@
# pi
A CLI project initializer in Rust
# pi

A CLI project initializer written in Rust.

## Table of Contents

* [Installation](#installation)
* [Usage](#usage)
* [Contributing](#contributing)
* [License](#license)
* [Additional Information](#additional-information)

## Installation

[Install Rust](https://www.rust-lang.org/tools/install) if you haven't, then clone this repo and build using the following commands:

```bash
git clone https://github.com/uptudev/pi
cd ./pi
cargo build --release
```

This will build your executable in the `./target/release/` folder, with the filename `pi` on Unix-like systems, and `pi.exe` on Windows. Simply move this file to wherever your OS pulls binaries from, and you can build projects with the `pi` command.

## Usage

The base initializer is run simply via `pi`, but if the project name and language are given, this can streamline the initialization process.

**Tips:**

* Break down instructions into logical steps.
* Use bullet points for succinct explanations.
* Consider creating a separate "Getting Started" guide for beginners.

## Contributing

**Outline your contribution guidelines.** Explain how users can contribute to your project, whether through code, bug reports, or documentation improvements. Specify preferred code style, pull request format, and testing procedures.

## License

**Specify the license under which your project is distributed.** Use clear and concise language, and link to the full license text in the `LICENSE` file.

## Additional Information

**Include any other relevant information you want to share.** This could be links to related projects, documentation, support channels, or your contact information.

**Remember:**

* Keep your README.md file concise and focused.
* Use clear headings, formatting, and visuals for readability.
* Update your README.md file regularly to reflect changes in your project.

16 changes: 16 additions & 0 deletions notes
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
IDEAS:

migrate to a modular architecture with language functions being in their own folders for readability (libs.rs is getting a bit bloated)

example file structure:
./src/
|-> main.rs
|-> libs/
|-> base.rs // contains query, routing, etc
|-> rust.rs
|-> js.rs
|-> ts.rs
|-> react.rs
|-> vue.rs
|-> svelte.rs
|-> mod.rs // module declaration
9 changes: 0 additions & 9 deletions src/libs.rs

This file was deleted.

208 changes: 208 additions & 0 deletions src/libs/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
use std::io::Write;
use colored::*;
use std::{io::{stdout, Result}, process::Command, fs};
use crate::libs::{
js, react, rust, svelte, ts, vue, zig
};

pub fn start(name: Option<String>, lang: Option<String>) {
let mag_colour_code = get_trailing_char();
let _ =
Command::new("clear")
.status();
let mut buffer = String::new();
title();
check_directory(&mut buffer, &mag_colour_code);

// option destruction syntax is hot garbage ngl
let proj_name = if let Some(n) = name {
n
} else {
get_project_name(&mut buffer, &mag_colour_code)
};
let lang = if let Some(l) = lang {
l
} else {
get_language(&mut buffer, &mag_colour_code)
};
route_response(&mut buffer, lang, &mag_colour_code, &proj_name);
stdout()
.flush()
.expect("Error flushing stdout.");
}

/// prints the title
pub fn title() {
println!("{}", "┍━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┑".black().dimmed());
println!(
"{} {} {} {} {}",
"│".black().dimmed(),
"Project Initializer".bold().underline(),
"by".normal(),
"uptu".bold().cyan(),
"│".black().dimmed());
println!("{}", "┕━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┙".black().dimmed());
println!("{}", "Please ensure you are in the parent directory of your intended project location before proceeding.".bold().red());
let cwd = std::env::current_dir()
.expect("Error getting CWD")
.as_path()
.as_os_str()
.to_str()
.unwrap()
.to_owned();
println!("{} {}","CWD:".purple().dimmed(), cwd.purple());
}

/// queries the user with a prompt, returning an owned String
pub fn query(prompt: &String, buffer: &mut String, mag: &str) -> String {
print!("{}{}", prompt, mag);
std::io::stdout().flush().expect("Error flushing buffer");
std::io::stdin()
.read_line(buffer)
.expect("Failed to read input to buffer");
buffer.trim().to_owned()
}

/// Gets system magenta colour code via tput
pub fn get_trailing_char() -> String {
let mut magenta_res = std::process::Command::new("tput");
magenta_res.args(["setaf", "5"]);
let res = magenta_res.output().unwrap();
String::from_utf8(res.stdout).unwrap()
}

/// queries the user to proceed after listing CWD
pub fn check_directory(buffer: &mut String, mag: &str) {
let prompt = format!(
"{} ({}/{}): ",
"Proceed?".yellow(),
"Y".bold().green(),
"N".red());

let response = query(&prompt, buffer, mag);
match response.as_str() {
"y" | "Y" | "" => println!(""),
"n" | "N" => clear_exit(),
_ => {
buffer.clear();
println!(
"{}; please enter {} or {}.",
"Invalid response".bold().red(),
"Y".bold().green(),
"N".bold().red());
check_directory(buffer, mag);
},
}
}

fn clear_exit() {
let _ =
std::process::Command::new("clear")
.status();
std::process::exit(0);
}

/// helper fn for get_language; handles queried response
fn route_response(buffer: &mut String, response: String, mag: &str, proj_name: &String) {
match response.as_str() {
"ls" => list_languages(buffer, mag),
"rust" => rust::init(buffer, mag, proj_name),
"js" => js::init(buffer),
"ts" => ts::init(buffer),
"react" => react::init(buffer),
"vue" => vue::init(buffer),
"svelte" => svelte::init(buffer),
"zig" => zig::init(buffer),
_ => {
buffer.clear();
println!(
"{}; please enter a valid language.\n{} {} {}",
"Invalid response".bold().red(),
"(or type".black().dimmed(),
"ls".purple(),
"to list all valid languages)".black().dimmed());
get_language(buffer, mag);
},
}
}

/// query user for the project language, then run the corresponding language_init() function
pub fn get_language(buffer: &mut String, mag: &str) -> String {
buffer.clear();
let prompt = format!(
"{} {} {}: ",
"What".yellow(),
"language".cyan().bold(),
"is this project for?".yellow());
query(&prompt, buffer, mag).to_lowercase()
}

/// called when 'ls' is given as a response to `get_language()`
fn list_languages(buffer: &mut String, mag: &str) {
println!("{}",
"rust\njs\nts\nreact\nvue\nsvelte\n"
.blue()
.dimmed());
get_language(buffer, mag);
}

/// queries user for project name, returns an owned string
pub fn get_project_name(buffer: &mut String, mag: &str) -> String {
buffer.clear();
let prompt = format!(
"{}{}: ",
"Please enter the ".yellow(),
"project name".cyan().bold());
query(&prompt, buffer, mag).to_string()
}

/// creates a README.md in the target directory in a useful format
pub fn gen_readme(proj_name: &String, dir: &std::path::Path) -> Result<()>{
let mut base_readme: String = r#"#
**[Short, memorable description of your project]**
## Table of Contents
* [Installation](#installation)
* [Usage](#usage)
* [Contributing](#contributing)
* [License](#license)
* [Additional Information](#additional-information)
## Installation
**Clearly describe how to install your project.** This may involve specifying dependencies, prerequisites, and build instructions. Use code blocks, links, and step-by-step guides for clarity.
## Usage
**Provide clear and concise instructions on how to use your project.** Explain its functionalities, features, and common use cases. Include examples, screenshots, or GIFs if helpful.
**Tips:**
* Break down instructions into logical steps.
* Use bullet points for succinct explanations.
* Consider creating a separate "Getting Started" guide for beginners.
## Contributing
**Outline your contribution guidelines.** Explain how users can contribute to your project, whether through code, bug reports, or documentation improvements. Specify preferred code style, pull request format, and testing procedures.
## License
**Specify the license under which your project is distributed.** Use clear and concise language, and link to the full license text in the `LICENSE` file.
## Additional Information
**Include any other relevant information you want to share.** This could be links to related projects, documentation, support channels, or your contact information.
**Remember:**
* Keep your README.md file concise and focused.
* Use clear headings, formatting, and visuals for readability.
* Update your README.md file regularly to reflect changes in your project.
"#.to_string();
base_readme.insert_str(2, proj_name);
let mut file = fs::File::create(dir)?;
file.write_all(base_readme.as_bytes())
}
4 changes: 4 additions & 0 deletions src/libs/js.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn init(buffer: &mut String) {
todo!("JavaScript init script needed");
}

8 changes: 8 additions & 0 deletions src/libs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pub mod base;
pub mod js;
pub mod react;
pub mod rust;
pub mod svelte;
pub mod ts;
pub mod vue;
pub mod zig;
4 changes: 4 additions & 0 deletions src/libs/react.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn init(buffer: &mut String) {
todo!("React init script needed");
}

50 changes: 50 additions & 0 deletions src/libs/rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use colored::*;
use crate::libs::base::{query, gen_readme};

fn get_is_lib(buffer: &mut String, mag: &str) -> String {
buffer.clear();
let prompt = format!(
"{} {} {} {} {}: ",
"Is this a".yellow(),
"binary".cyan().bold(),
"or".yellow(),
"library".cyan().bold(),
"project?".yellow());
let response = query(&prompt, buffer, mag);
match response.as_str() {
"b" | "bin" | "binary" => "--bin".to_string(),
"l" | "lib" | "library" => "--lib".to_string(),
_ => get_is_lib(buffer, mag),
}
}

pub fn init(buffer: &mut String, mag: &str, proj_name: &String) {
let is_lib = get_is_lib(buffer, mag);
let cwd = std::env::current_dir()
.expect("Error getting CWD")
.as_path()
.as_os_str()
.to_str()
.unwrap()
.to_owned();
println!(
"{}{}{}{}{}",
"Creating Rust project ".purple().dimmed(),
proj_name.purple().bold(),
" in ".purple().dimmed(),
cwd.purple(),
"...".purple().dimmed());
let mut handle = std::process::Command::new("cargo")
.args(["new", proj_name, &is_lib, "--quiet"])
.spawn()
.expect("Error spawning child process.");
let exit_status = handle.wait().unwrap();
let proj_dir = format!("./{}", proj_name);
let readme_dir = proj_dir + "/README.md";
let readme_path = std::path::Path::new(&readme_dir);
gen_readme(&proj_name, &readme_path).unwrap();
if exit_status.success() {
println!("{}", "Done!".green().bold());
}
}

3 changes: 3 additions & 0 deletions src/libs/svelte.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn init(buffer: &mut String) {
todo!("Svelte init script needed");
}
4 changes: 4 additions & 0 deletions src/libs/ts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn init(buffer: &mut String) {
todo!("TypeScript init script needed");
}

Loading

0 comments on commit 4fb9764

Please sign in to comment.