├── .gitignore ├── CODEOWNERS ├── src ├── error_macro.rs ├── cli.rs ├── main.rs ├── utils.rs ├── progress_bar.rs ├── parser.rs ├── version.rs └── command.rs ├── CHANGELOG.md ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── CICD.yml ├── man └── tmplt.1 ├── docs ├── build_from_source.md ├── project_layout.md └── usage.md ├── LICENSE-MIT ├── Cargo.toml ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── completions ├── tmplt.fish ├── _tmplt.ps1 ├── _tmplt └── tmplt.bash ├── README.md ├── LICENSE-APACHE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /build 3 | 4 | /.vscode 5 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # default owners for everything 2 | * @humblepenguinn 3 | -------------------------------------------------------------------------------- /src/error_macro.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! error { 3 | ($msg:expr) => {{ 4 | eprint!("\x1b[31mError: \x1b[0m{}\n", $msg); 5 | }}; 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | This document records all notable changes to [tmplt](https://github.com/humblepenguinn/tmplt). 4 | 5 | # v0.1.0 6 | 7 | - Inital Release 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 2 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /man/tmplt.1: -------------------------------------------------------------------------------- 1 | .ie \n(.g .ds Aq \(aq 2 | .el .ds Aq ' 3 | .TH tmplt 1 "tmplt " 4 | .SH NAME 5 | tmplt 6 | .SH SYNOPSIS 7 | \fBtmplt\fR [\fB\-h\fR|\fB\-\-help\fR] <\fIsubcommands\fR> 8 | .SH DESCRIPTION 9 | .SH OPTIONS 10 | .TP 11 | \fB\-h\fR, \fB\-\-help\fR 12 | Print help 13 | .SH SUBCOMMANDS 14 | .TP 15 | tmplt\-new(1) 16 | Create a new project 17 | .TP 18 | tmplt\-init(1) 19 | Create a new template 20 | .TP 21 | tmplt\-import(1) 22 | Import a template 23 | .TP 24 | tmplt\-list(1) 25 | List all templates 26 | .TP 27 | tmplt\-remove(1) 28 | Remove a template 29 | .TP 30 | tmplt\-version(1) 31 | Show version 32 | .TP 33 | tmplt\-help(1) 34 | Print this message or the help of the given subcommand(s) 35 | -------------------------------------------------------------------------------- /docs/build_from_source.md: -------------------------------------------------------------------------------- 1 | # Build from source 2 | 3 | Verify that you have Rust installed: 4 | 5 | ```sh 6 | $ rustc --version 7 | ``` 8 | 9 | If `rust` is not installed, follow the instructions on [the Offical Rust website](https://www.rust-lang.org/tools/install). 10 | 11 | Clone this repository: 12 | ```sh 13 | $ git clone https://github.com/humblepenguinn/tmplt.git 14 | $ cd tmplt 15 | ``` 16 | 17 | Build the project: 18 | 19 | ```sh 20 | $ cargo build 21 | ``` 22 | 23 | Now, Check to see if it worked: 24 | ```sh 25 | $ cargo run -- version 26 | ``` 27 | 28 | You can also install the project: 29 | 30 | ```sh 31 | $ cargo install --path . 32 | $ tmplt version 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Run command '...' 16 | 2. Go to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Device (please complete the following information):** 26 | - OS: [e.g. iOS] 27 | - Shell [e.g. zsh, bash, powershell] 28 | - CLI Version [e.g. 0.1.0] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::Args; 2 | use clap::Parser; 3 | 4 | #[derive(Parser)] 5 | pub struct Cli { 6 | #[command(subcommand)] 7 | pub command: Command, 8 | } 9 | 10 | #[derive(Debug, Args)] 11 | pub struct CommandArgs { 12 | pub args: Vec, 13 | } 14 | 15 | #[derive(clap::Subcommand, Debug)] 16 | pub enum Command { 17 | #[clap(name = "new", about = "Create a new project")] 18 | New(CommandArgs), 19 | #[clap(name = "init", about = "Create a new template")] 20 | Init(CommandArgs), 21 | #[clap(name = "import", about = "Import a template")] 22 | Import(CommandArgs), 23 | #[clap(name = "list", about = "List all templates")] 24 | List, 25 | #[clap(name = "remove", about = "Remove a template")] 26 | Remove(CommandArgs), 27 | #[clap(name = "version", about = "Show version")] 28 | Version(CommandArgs), 29 | } 30 | -------------------------------------------------------------------------------- /docs/project_layout.md: -------------------------------------------------------------------------------- 1 | # Project Layout 2 | 3 | The project structure of the Rust CLI tool Tmplt can be explained as follows: 4 | 5 | ## src folder 6 | 7 | The `src` folder contains the source code for the `tmplt` CLI tool. It contains the following files: 8 | 9 | - ``cli.rs`: This file contains the code for defining the command line interface (CLI) using the `clap` crate. 10 | - `command.rs`: This file contains the code that implements the subcommands of the `tmplt` CLI tool. 11 | - `error_macro.rs`: This file contains a custom macro for generating error messages. 12 | - `main.rs`: This file contains the main function that is executed when `tmplt` is run. 13 | - `parser.rs`: This file contains the code for parsing template files. 14 | - `progress_bar.rs`: This file contains the code for displaying progress spinner while the template is running. 15 | - `utils.rs`: This file contains utility functions used throughout the project. 16 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Humble Penguin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tmplt" 3 | version = "0.1.1" 4 | rust-version = "1.64.0" 5 | description = "tmplt is a command-line interface tool that allows you to quickly and easily set up project templates for various programming languages and frameworks" 6 | edition = "2021" 7 | authors = ["Humble Penguin "] 8 | repository = "https://github.com/humblepenguinn/tmplt/" 9 | license = "MIT/Apache-2.0" 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | bincode = "1.3.3" 14 | chrono = { version = "0.4.20", features = ["serde"] } 15 | clap = { version = "4.1.12", features = ["derive"] } 16 | colored = "2.0.0" 17 | dirs = "5.0.0" 18 | indicatif = "0.17.3" 19 | inquire = "0.6.0" 20 | semver = "1.0.17" 21 | serde = { version = "1.0.159", features = ["derive"] } 22 | serde_json = "1.0.95" 23 | serde_yaml = "0.9.19" 24 | tokio = "1.27.0" 25 | url = "2.3.1" 26 | 27 | [dependencies.reqwest] 28 | version = "0.11.16" 29 | default-features = false 30 | features = ["rustls-tls-native-roots"] 31 | 32 | [build-dependencies] 33 | chrono = "0.4.20" 34 | clap = { version = "4.1.12", features = ["derive"] } 35 | clap_mangen = "0.2.9" 36 | clap_complete = "4.2.0" 37 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod cli; 2 | mod command; 3 | mod error_macro; 4 | mod parser; 5 | mod progress_bar; 6 | mod utils; 7 | mod version; 8 | 9 | use std::path::Path; 10 | 11 | use clap::Parser; 12 | use colored::Colorize; 13 | use semver::Version; 14 | 15 | use crate::cli::Cli; 16 | use crate::version::get_latest_version; 17 | 18 | fn main() { 19 | let latest_version = get_latest_version(); 20 | 21 | let current_version = if let Ok(val) = Version::parse(env!("BUILD_VERSION")) { 22 | val 23 | } else { 24 | println!("{}: Failed to parse current version", "Error".red()); 25 | return; 26 | }; 27 | 28 | if latest_version > current_version { 29 | println!( 30 | "{}: {} -> {}", 31 | "New version available".yellow(), 32 | current_version, 33 | latest_version 34 | ); 35 | } 36 | 37 | if !Path::new(&utils::get_configdir()).exists() { 38 | println!("{}", "Creating config directory".bold()); 39 | if let Err(e) = std::fs::create_dir(utils::get_configdir()) { 40 | println!("{}: {}", "Error".red(), e); 41 | std::process::exit(1); 42 | } 43 | 44 | if let Err(e) = std::fs::create_dir(utils::get_configdir().join("templates")) { 45 | println!("{}: {}", "Error".red(), e); 46 | std::process::exit(1); 47 | } 48 | } 49 | 50 | let args = Cli::parse(); 51 | args.command.run(); 52 | } 53 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Write; 3 | use std::path::PathBuf; 4 | 5 | use indicatif::{ProgressBar, ProgressStyle}; 6 | use reqwest::Client; 7 | 8 | use crate::error; 9 | 10 | pub fn get_homedir() -> PathBuf { 11 | match dirs::home_dir() { 12 | Some(home) => home, 13 | None => { 14 | error!("Home directory not found"); 15 | std::process::exit(1); 16 | } 17 | } 18 | } 19 | 20 | pub fn get_configdir() -> PathBuf { 21 | let homedir = get_homedir(); 22 | homedir.join(".tmplt") 23 | } 24 | 25 | pub async fn download_file(url: &str, file_name: &str) { 26 | let client = Client::new(); 27 | let mut resp = match client.get(url).send().await { 28 | Ok(resp) => resp, 29 | Err(e) => { 30 | error!(e.to_string()); 31 | std::process::exit(1); 32 | } 33 | }; 34 | 35 | let mut file = match File::create(file_name) { 36 | Ok(file) => file, 37 | Err(e) => { 38 | error!(e.to_string()); 39 | std::process::exit(1); 40 | } 41 | }; 42 | 43 | let mut content_length = match resp.content_length() { 44 | Some(length) => length, 45 | None => { 46 | error!("Can not get content length"); 47 | std::process::exit(1); 48 | } 49 | }; 50 | 51 | let pb = ProgressBar::new(content_length); 52 | 53 | pb.set_style(ProgressStyle::default_bar() 54 | .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})") 55 | .unwrap() 56 | .progress_chars("#>-")); 57 | 58 | while let Some(chunk) = resp.chunk().await.unwrap() { 59 | let chunk_size = chunk.len(); 60 | if let Err(e) = file.write_all(&chunk) { 61 | error!(e.to_string()); 62 | std::process::exit(1); 63 | } 64 | 65 | pb.inc(chunk_size as u64); 66 | content_length -= chunk_size as u64; 67 | } 68 | 69 | pb.finish(); 70 | } 71 | -------------------------------------------------------------------------------- /src/progress_bar.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicBool, Ordering}; 2 | use std::sync::Arc; 3 | use std::sync::Mutex; 4 | 5 | use indicatif::{ProgressBar, ProgressStyle}; 6 | 7 | pub fn get_progress_bar() -> ProgressBar { 8 | let progress_bar = ProgressBar::new_spinner(); 9 | progress_bar.set_style(get_style()); 10 | 11 | progress_bar 12 | } 13 | 14 | pub fn get_style() -> ProgressStyle { 15 | ProgressStyle::with_template("{spinner:.green} {wide_msg}") 16 | .unwrap() 17 | .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ") 18 | } 19 | 20 | pub fn get_progress_bar_handle( 21 | done: Arc, 22 | progress_bar_arc: Arc>, 23 | msg: String, 24 | ) -> std::thread::JoinHandle<()> { 25 | std::thread::spawn({ 26 | move || { 27 | while !done.load(Ordering::SeqCst) { 28 | let progress_bar = progress_bar_arc.lock().unwrap(); 29 | progress_bar.set_message(msg.clone()); 30 | progress_bar.inc(1); 31 | std::thread::sleep(std::time::Duration::from_millis(100)); 32 | } 33 | done.store(false, Ordering::SeqCst); 34 | } 35 | }) 36 | } 37 | 38 | pub fn get_progress_bar_handle_with_rx( 39 | done: Arc, 40 | progress_bar_arc: Arc>, 41 | fallback_msg: String, 42 | rx: std::sync::mpsc::Receiver, 43 | ) -> std::thread::JoinHandle<()> { 44 | std::thread::spawn({ 45 | move || { 46 | let mut msg: Option = None; 47 | while !done.load(Ordering::SeqCst) { 48 | let progress_bar = progress_bar_arc.lock().unwrap(); 49 | 50 | if let Ok(new_msg) = rx.try_recv() { 51 | msg = Some(new_msg); 52 | } 53 | 54 | let progress_msg = &msg.as_ref().unwrap_or(&fallback_msg); 55 | 56 | progress_bar.set_message(progress_msg.to_string()); 57 | progress_bar.inc(1); 58 | std::thread::sleep(std::time::Duration::from_millis(100)); 59 | } 60 | done.store(false, Ordering::SeqCst); 61 | } 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Thanks for your interest in contributing to tmplt! 4 | 5 | Any form of contribution is accepted, be it bug fixes, implementing a new feature or even just fixing a small typo. The goal is to get the community involved as much as possible 6 | 7 | pull requests for bug fixes and features will only be accepted if the approach has been discussed in an issue and a community memeber has been given the go-ahead to work on it 8 | 9 | Please keep the following in mind at all times: 10 | 11 | * Check existing issues to verify that the [`bug`](https://github.com/humblepenguinn/tmplt/labels/bug) or [`feature request`](https://github.com/humblepenguinn/tmplt/labels/feature%20request) has not already been submitted. 12 | * Open an issue if things aren't working as expected. 13 | * Open an issue to propose a significant change. 14 | * Open a pull request to fix a bug. 15 | 16 | * Open a pull request for any issue labelled [`help wanted`](https://github.com/humblepenguinn/tmplt/labels/help%20wanted), [`good first issue`](https://github.com/humblepenguinn/tmplt/labels/good%20first%20issue) or [`community`](https://github.com/humblepenguinn/tmplt/labels/community). 17 | 18 | Please avoid: 19 | 20 | * Opening pull requests for issues marked `needs-triage`, `needs-investigation`, or `blocked`. 21 | * Opening pull requests for any issue marked `maintainers`. These issues require additional context from 22 | the maintainers/code owners and any external pull requests will not be accepted. 23 | 24 | ## Building the project 25 | See how to [build the editor from source here](./docs/build_from_source.md) 26 | 27 | See [project layout documentation](./docs/project_layout.md) for information on where to find specific source files. 28 | 29 | ## Tests 30 | Tests have not yet been written for `tmplt`, so maybe thats something you could create a pull request for? 31 | 32 | ## Submitting a pull request 33 | 34 | 1. Create a new branch: `git checkout -b my-branch-name` 35 | 2. Make your change 36 | 3. Run `cargo fmt --all --check` 37 | 4. Run `cargo clippy --fix --all-features` 38 | 5. Submit a pull request 39 | 40 | Contributions to this project are released to the public under the project's open source licenses, 41 | the [MIT License](LICENSE-MIT) and the [Apache License](LICENSE-APACHE) 42 | 43 | Please note that this project adheres to a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms. 44 | 45 | ## Design guidelines 46 | Let your imagination run wild and suggest amazing ideas 47 | 48 | There isn't any strict design guidelines yet. I am still working on that, so for now the project is open to any kind of change 49 | 50 | ## Resources 51 | 52 | - [How to Contribute to Open Source][] 53 | - [Using Pull Requests][] 54 | - [GitHub Help][] 55 | 56 | 57 | 58 | [code-of-conduct]: ./CODE_OF_CONDUCT.md 59 | [How to Contribute to Open Source]: https://opensource.guide/how-to-contribute/ 60 | [Using Pull Requests]: https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-pull-requests 61 | [GitHub Help]: https://docs.github.com/ 62 | 63 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | 78 | -------------------------------------------------------------------------------- /completions/tmplt.fish: -------------------------------------------------------------------------------- 1 | complete -c tmplt -n "__fish_use_subcommand" -s h -l help -d 'Print help' 2 | complete -c tmplt -n "__fish_use_subcommand" -f -a "new" -d 'Create a new project' 3 | complete -c tmplt -n "__fish_use_subcommand" -f -a "init" -d 'Create a new template' 4 | complete -c tmplt -n "__fish_use_subcommand" -f -a "import" -d 'Import a template' 5 | complete -c tmplt -n "__fish_use_subcommand" -f -a "list" -d 'List all templates' 6 | complete -c tmplt -n "__fish_use_subcommand" -f -a "remove" -d 'Remove a template' 7 | complete -c tmplt -n "__fish_use_subcommand" -f -a "version" -d 'Show version' 8 | complete -c tmplt -n "__fish_use_subcommand" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' 9 | complete -c tmplt -n "__fish_seen_subcommand_from new" -s h -l help -d 'Print help' 10 | complete -c tmplt -n "__fish_seen_subcommand_from init" -s h -l help -d 'Print help' 11 | complete -c tmplt -n "__fish_seen_subcommand_from import" -s h -l help -d 'Print help' 12 | complete -c tmplt -n "__fish_seen_subcommand_from list" -s h -l help -d 'Print help' 13 | complete -c tmplt -n "__fish_seen_subcommand_from remove" -s h -l help -d 'Print help' 14 | complete -c tmplt -n "__fish_seen_subcommand_from version" -s h -l help -d 'Print help' 15 | complete -c tmplt -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from new; and not __fish_seen_subcommand_from init; and not __fish_seen_subcommand_from import; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from remove; and not __fish_seen_subcommand_from version; and not __fish_seen_subcommand_from help" -f -a "new" -d 'Create a new project' 16 | complete -c tmplt -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from new; and not __fish_seen_subcommand_from init; and not __fish_seen_subcommand_from import; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from remove; and not __fish_seen_subcommand_from version; and not __fish_seen_subcommand_from help" -f -a "init" -d 'Create a new template' 17 | complete -c tmplt -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from new; and not __fish_seen_subcommand_from init; and not __fish_seen_subcommand_from import; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from remove; and not __fish_seen_subcommand_from version; and not __fish_seen_subcommand_from help" -f -a "import" -d 'Import a template' 18 | complete -c tmplt -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from new; and not __fish_seen_subcommand_from init; and not __fish_seen_subcommand_from import; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from remove; and not __fish_seen_subcommand_from version; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List all templates' 19 | complete -c tmplt -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from new; and not __fish_seen_subcommand_from init; and not __fish_seen_subcommand_from import; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from remove; and not __fish_seen_subcommand_from version; and not __fish_seen_subcommand_from help" -f -a "remove" -d 'Remove a template' 20 | complete -c tmplt -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from new; and not __fish_seen_subcommand_from init; and not __fish_seen_subcommand_from import; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from remove; and not __fish_seen_subcommand_from version; and not __fish_seen_subcommand_from help" -f -a "version" -d 'Show version' 21 | complete -c tmplt -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from new; and not __fish_seen_subcommand_from init; and not __fish_seen_subcommand_from import; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from remove; and not __fish_seen_subcommand_from version; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' 22 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | Before reading the usage make sure you understand what `templates` (See [templates](https://github.com/humblepenguinn/tmplt#templates)) are in `tmplt` 3 | 4 | ## Creating new Template 5 | To use `tmplt`, users need to first create or [import](#import-a-template) a template. Running `tmplt init ` will generate a new template with the provided name, which you can then find in the tools config directory `HOME_DIR/.tmplt/templates` (note that `HOME_DIR` refers to the operating systems home directory). You can now modify this template using a text editor. 6 | 7 | Lets create a new `flask app` template using `tmplt` 8 | 9 | ```sh 10 | $ tmplt init flask-app 11 | ``` 12 | 13 | This will create a new template named "flask-app" in the `HOME_DIR/.tmplt/templates` directory. 14 | 15 | Open the template with a text editor. This step is specific to your operating system and text editor of choice 16 | 17 | When you open the template file using a text editor you should see the following contents: 18 | ```yaml 19 | # Go to the GitHub repo https://github.com/humblepenguinn/tmplt for more information 20 | 21 | # Template information 22 | name: flask-app 23 | description: Description of the template 24 | 25 | # Commands to run during setup 26 | setup_commands: 27 | - command_1 28 | - command_2 29 | 30 | # Dependency information 31 | dependencies: 32 | - name: dependency_1 33 | install_command: install_command_1 34 | 35 | - name: dependency_2 36 | install_command: install_command_2 37 | 38 | # Files to generate 39 | files: 40 | - name: file_name_1 41 | content: | 42 | file contents 43 | - name: file_name_2 44 | content: | 45 | file contents 46 | 47 | # Post-setup command to run after setup is complete 48 | post_setup_commands: 49 | - command_1 50 | - command_2 51 | 52 | variables: 53 | - name: my_variable_name 54 | description: description of the variable 55 | default: the default value of the variable 56 | ``` 57 | 58 | Modify the contents of the template: 59 | ```yaml 60 | # Flask Project Template 61 | 62 | # Template information 63 | name: Flask Project 64 | description: A template for creating a Flask project 65 | 66 | # Dependency information 67 | dependencies: 68 | - name: Flask 69 | install_command: pip install flask 70 | 71 | # Files to generate 72 | files: 73 | - name: app.py 74 | content: | 75 | from flask import Flask 76 | 77 | app = Flask(__name__) 78 | 79 | @app.route("/") 80 | def hello_world(): 81 | return "

Hello, World from {app_name}!

" 82 | 83 | if __name__ == "__main__": 84 | app.run() 85 | 86 | 87 | # Variables 88 | variables: 89 | - name: app_name 90 | description: The name of the Flask app 91 | default: my_flask_app 92 | 93 | ``` 94 | 95 | We now have successfully created our first template! 96 | 97 | ## Create a new project 98 | To create a new project using a template, users can run the `tmplt create ` command. This command will create a new directory with the name specified in the `` argument, generate all the files, download all the dependencies and run all the `post` and `setup` commands specified in the template. 99 | 100 | If the template has any `variables`, `tmplt` will prompt you to provide a value for each variable. You can choose to use the default value or provide a new value. 101 | 102 | Let's use our flask template to create a new flask project: 103 | ```sh 104 | $ tmplt create myflaskapp flask-app 105 | ``` 106 | 107 | This will create a new directory with the name `myflaskapp`, based on the `flask-app` template we created earlier. The command will prompt you to provide a value for the `app_name` variable. 108 | 109 | ## Import a Template 110 | 111 | In addition to being able to create your own templates, users can also import templates created by other users! 112 | 113 | The `tmplt import ` command is used to import a template from a URL. Running `tmplt import` will download the template from the URL and save it with the name you provided. 114 | 115 | You can then use the imported template to create a new project as showed in the [Create a new project section](#create-a-new-project) 116 | 117 | ## List Available Templates 118 | This command is used to list all the available templates. Running `tmplt list` will display a list of all the templates that you can use to create projects. 119 | 120 | ```sh 121 | tmplt list 122 | ``` 123 | 124 | That's it! With these simple commands, you can use `tmplt` to create and manage projects based on templates. 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # tmplt 3 | 4 | [![CICD](https://github.com/humblepenguinn/tmplt/actions/workflows/CICD.yml/badge.svg)](https://github.com/humblepenguinn/tmplt/workflows/CICD.yml) 5 | [![Version info](https://img.shields.io/crates/v/tmplt.svg)](https://crates.io/crates/tmplt) 6 | 7 | A User Friendly CLI Tool For Creating New Projects With Templates 8 | 9 | 10 | Demo 11 | 12 | ## About 13 | 14 | `tmplt` is a command-line tool that lets users quickly create new projects based on templates. With `tmplt`, users can create a new project that is set up with all the necessary files and dependencies, so they can get started on their project right away. 15 | 16 | With `tmplt`, users can create templates that define the structure and dependencies of a new project. These templates can be customized to fit specific project needs and shared with others. `tmplt` comes with a simple yet flexible syntax for defining `templates` that can include variables, files, and dependencies. 17 | 18 | `Templates` are defined in a `yaml` file that lists all the files to be generated, the dependencies to be installed, and the variables to be replaced. 19 | 20 | ## Templates 21 | `Templates` are files that contain information about the project, its dependencies, files to generate, and variables. Users can use these templates to set up projects easily. 22 | 23 | A `template` file is written in `YAML` format and contains the following information: 24 | 25 | * Name: The name of the template. 26 | 27 | * Description: A short description of the template. 28 | 29 | * Dependencies: A list of dependencies required by the project. Each dependency contains a name and an install command. 30 | 31 | * Files: A list of files to generate for the project. Each file contains a name and the content to be 32 | written to the file. 33 | 34 | * Variables: A list of variables that can be used in the template. Each variable contains a name, description, and default value. 35 | 36 | Users can create their own `templates` or download them from the internet. To create a new project from a `template`, simply run the `tmplt new` command and provide the name of the template. The tool will generate all the necessary files and install the required dependencies. See [usage.md](./docs/usage.md) for more information on how to use the tool 37 | 38 | Here's an example `template` for a Pygame project: 39 | 40 | ```yaml 41 | # Pygame Project Template 42 | 43 | # Template information 44 | name: Pygame Project 45 | description: A template for creating a Pygame project 46 | 47 | # Dependency information 48 | dependencies: 49 | - name: Pygame 50 | install_command: pip install pygame 51 | 52 | # Files to generate 53 | files: 54 | - name: main.py 55 | content: | 56 | import pygame 57 | 58 | # Set up pygame 59 | pygame.init() 60 | 61 | # Set up the display 62 | screen_width = {screen_width} 63 | screen_height = {screen_height} 64 | screen = pygame.display.set_mode((screen_width, screen_height)) 65 | pygame.display.set_caption("{app_name}") 66 | 67 | # Game loop 68 | running = True 69 | while running: 70 | # Event handling 71 | for event in pygame.event.get(): 72 | if event.type == pygame.QUIT: 73 | running = False 74 | 75 | # Game logic 76 | 77 | # Draw to screen 78 | screen.fill((255, 255, 255)) 79 | pygame.display.flip() 80 | 81 | # Clean up pygame 82 | pygame.quit() 83 | 84 | 85 | # Variables 86 | variables: 87 | - name: app_name 88 | description: The name of the pygame app 89 | default: my_pygame_app 90 | 91 | - name: screen_width 92 | description: The screen width of the pygame app 93 | default: 800 94 | 95 | - name: screen_height 96 | description: The screen height of the pygame app 97 | default: 600 98 | ``` 99 | 100 | ## Installation 101 | 102 | You can install `tmplt` through a few methods 103 | 104 | ### Releases 105 | 106 | You can head over to the [releases page](https://github.com/humblepenguinn/tmplt/releases/latest) and download the official `tmplt` binaries from there for your target operating system. `Windows MSI installers` are also available 107 | 108 | ### Cargo Repository 109 | 110 | You can install `tmplt` through the Cargo repository using the following command: 111 | 112 | ```sh 113 | $ cargo install tmplt 114 | ``` 115 | 116 | ### Source 117 | 118 | Go [here](./docs/build_from_source.md) to see how 119 | 120 | More methods of installation will be added in the future! 121 | 122 | ## Usage 123 | 124 | Go [here](./docs/usage.md) to see how to use the tool 125 | 126 | 127 | ## Contributing 128 | 129 | Contributions to `tmplt` are always welcome! Please see the [Contributing Guidelines](CONTRIBUTING.md) for more information. 130 | 131 | ## License 132 | 133 | This project is licensed under the [MIT](LICENSE-MIT) License and the [Apache](LICENSE-APACHE) License 134 | -------------------------------------------------------------------------------- /completions/_tmplt.ps1: -------------------------------------------------------------------------------- 1 | 2 | using namespace System.Management.Automation 3 | using namespace System.Management.Automation.Language 4 | 5 | Register-ArgumentCompleter -Native -CommandName 'tmplt' -ScriptBlock { 6 | param($wordToComplete, $commandAst, $cursorPosition) 7 | 8 | $commandElements = $commandAst.CommandElements 9 | $command = @( 10 | 'tmplt' 11 | for ($i = 1; $i -lt $commandElements.Count; $i++) { 12 | $element = $commandElements[$i] 13 | if ($element -isnot [StringConstantExpressionAst] -or 14 | $element.StringConstantType -ne [StringConstantType]::BareWord -or 15 | $element.Value.StartsWith('-') -or 16 | $element.Value -eq $wordToComplete) { 17 | break 18 | } 19 | $element.Value 20 | }) -join ';' 21 | 22 | $completions = @(switch ($command) { 23 | 'tmplt' { 24 | [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') 25 | [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') 26 | [CompletionResult]::new('new', 'new', [CompletionResultType]::ParameterValue, 'Create a new project') 27 | [CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Create a new template') 28 | [CompletionResult]::new('import', 'import', [CompletionResultType]::ParameterValue, 'Import a template') 29 | [CompletionResult]::new('list', 'list', [CompletionResultType]::ParameterValue, 'List all templates') 30 | [CompletionResult]::new('remove', 'remove', [CompletionResultType]::ParameterValue, 'Remove a template') 31 | [CompletionResult]::new('version', 'version', [CompletionResultType]::ParameterValue, 'Show version') 32 | [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Print this message or the help of the given subcommand(s)') 33 | break 34 | } 35 | 'tmplt;new' { 36 | [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') 37 | [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') 38 | break 39 | } 40 | 'tmplt;init' { 41 | [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') 42 | [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') 43 | break 44 | } 45 | 'tmplt;import' { 46 | [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') 47 | [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') 48 | break 49 | } 50 | 'tmplt;list' { 51 | [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') 52 | [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') 53 | break 54 | } 55 | 'tmplt;remove' { 56 | [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') 57 | [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') 58 | break 59 | } 60 | 'tmplt;version' { 61 | [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') 62 | [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') 63 | break 64 | } 65 | 'tmplt;help' { 66 | [CompletionResult]::new('new', 'new', [CompletionResultType]::ParameterValue, 'Create a new project') 67 | [CompletionResult]::new('init', 'init', [CompletionResultType]::ParameterValue, 'Create a new template') 68 | [CompletionResult]::new('import', 'import', [CompletionResultType]::ParameterValue, 'Import a template') 69 | [CompletionResult]::new('list', 'list', [CompletionResultType]::ParameterValue, 'List all templates') 70 | [CompletionResult]::new('remove', 'remove', [CompletionResultType]::ParameterValue, 'Remove a template') 71 | [CompletionResult]::new('version', 'version', [CompletionResultType]::ParameterValue, 'Show version') 72 | [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Print this message or the help of the given subcommand(s)') 73 | break 74 | } 75 | 'tmplt;help;new' { 76 | break 77 | } 78 | 'tmplt;help;init' { 79 | break 80 | } 81 | 'tmplt;help;import' { 82 | break 83 | } 84 | 'tmplt;help;list' { 85 | break 86 | } 87 | 'tmplt;help;remove' { 88 | break 89 | } 90 | 'tmplt;help;version' { 91 | break 92 | } 93 | 'tmplt;help;help' { 94 | break 95 | } 96 | }) 97 | 98 | $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | 99 | Sort-Object -Property ListItemText 100 | } 101 | -------------------------------------------------------------------------------- /completions/_tmplt: -------------------------------------------------------------------------------- 1 | #compdef tmplt 2 | 3 | autoload -U is-at-least 4 | 5 | _tmplt() { 6 | typeset -A opt_args 7 | typeset -a _arguments_options 8 | local ret=1 9 | 10 | if is-at-least 5.2; then 11 | _arguments_options=(-s -S -C) 12 | else 13 | _arguments_options=(-s -C) 14 | fi 15 | 16 | local context curcontext="$curcontext" state line 17 | _arguments "${_arguments_options[@]}" \ 18 | '-h[Print help]' \ 19 | '--help[Print help]' \ 20 | ":: :_tmplt_commands" \ 21 | "*::: :->tmplt" \ 22 | && ret=0 23 | case $state in 24 | (tmplt) 25 | words=($line[1] "${words[@]}") 26 | (( CURRENT += 1 )) 27 | curcontext="${curcontext%:*:*}:tmplt-command-$line[1]:" 28 | case $line[1] in 29 | (new) 30 | _arguments "${_arguments_options[@]}" \ 31 | '-h[Print help]' \ 32 | '--help[Print help]' \ 33 | '*::args:' \ 34 | && ret=0 35 | ;; 36 | (init) 37 | _arguments "${_arguments_options[@]}" \ 38 | '-h[Print help]' \ 39 | '--help[Print help]' \ 40 | '*::args:' \ 41 | && ret=0 42 | ;; 43 | (import) 44 | _arguments "${_arguments_options[@]}" \ 45 | '-h[Print help]' \ 46 | '--help[Print help]' \ 47 | '*::args:' \ 48 | && ret=0 49 | ;; 50 | (list) 51 | _arguments "${_arguments_options[@]}" \ 52 | '-h[Print help]' \ 53 | '--help[Print help]' \ 54 | && ret=0 55 | ;; 56 | (remove) 57 | _arguments "${_arguments_options[@]}" \ 58 | '-h[Print help]' \ 59 | '--help[Print help]' \ 60 | '*::args:' \ 61 | && ret=0 62 | ;; 63 | (version) 64 | _arguments "${_arguments_options[@]}" \ 65 | '-h[Print help]' \ 66 | '--help[Print help]' \ 67 | '*::args:' \ 68 | && ret=0 69 | ;; 70 | (help) 71 | _arguments "${_arguments_options[@]}" \ 72 | ":: :_tmplt__help_commands" \ 73 | "*::: :->help" \ 74 | && ret=0 75 | 76 | case $state in 77 | (help) 78 | words=($line[1] "${words[@]}") 79 | (( CURRENT += 1 )) 80 | curcontext="${curcontext%:*:*}:tmplt-help-command-$line[1]:" 81 | case $line[1] in 82 | (new) 83 | _arguments "${_arguments_options[@]}" \ 84 | && ret=0 85 | ;; 86 | (init) 87 | _arguments "${_arguments_options[@]}" \ 88 | && ret=0 89 | ;; 90 | (import) 91 | _arguments "${_arguments_options[@]}" \ 92 | && ret=0 93 | ;; 94 | (list) 95 | _arguments "${_arguments_options[@]}" \ 96 | && ret=0 97 | ;; 98 | (remove) 99 | _arguments "${_arguments_options[@]}" \ 100 | && ret=0 101 | ;; 102 | (version) 103 | _arguments "${_arguments_options[@]}" \ 104 | && ret=0 105 | ;; 106 | (help) 107 | _arguments "${_arguments_options[@]}" \ 108 | && ret=0 109 | ;; 110 | esac 111 | ;; 112 | esac 113 | ;; 114 | esac 115 | ;; 116 | esac 117 | } 118 | 119 | (( $+functions[_tmplt_commands] )) || 120 | _tmplt_commands() { 121 | local commands; commands=( 122 | 'new:Create a new project' \ 123 | 'init:Create a new template' \ 124 | 'import:Import a template' \ 125 | 'list:List all templates' \ 126 | 'remove:Remove a template' \ 127 | 'version:Show version' \ 128 | 'help:Print this message or the help of the given subcommand(s)' \ 129 | ) 130 | _describe -t commands 'tmplt commands' commands "$@" 131 | } 132 | (( $+functions[_tmplt__help_commands] )) || 133 | _tmplt__help_commands() { 134 | local commands; commands=( 135 | 'new:Create a new project' \ 136 | 'init:Create a new template' \ 137 | 'import:Import a template' \ 138 | 'list:List all templates' \ 139 | 'remove:Remove a template' \ 140 | 'version:Show version' \ 141 | 'help:Print this message or the help of the given subcommand(s)' \ 142 | ) 143 | _describe -t commands 'tmplt help commands' commands "$@" 144 | } 145 | (( $+functions[_tmplt__help__help_commands] )) || 146 | _tmplt__help__help_commands() { 147 | local commands; commands=() 148 | _describe -t commands 'tmplt help help commands' commands "$@" 149 | } 150 | (( $+functions[_tmplt__help__import_commands] )) || 151 | _tmplt__help__import_commands() { 152 | local commands; commands=() 153 | _describe -t commands 'tmplt help import commands' commands "$@" 154 | } 155 | (( $+functions[_tmplt__import_commands] )) || 156 | _tmplt__import_commands() { 157 | local commands; commands=() 158 | _describe -t commands 'tmplt import commands' commands "$@" 159 | } 160 | (( $+functions[_tmplt__help__init_commands] )) || 161 | _tmplt__help__init_commands() { 162 | local commands; commands=() 163 | _describe -t commands 'tmplt help init commands' commands "$@" 164 | } 165 | (( $+functions[_tmplt__init_commands] )) || 166 | _tmplt__init_commands() { 167 | local commands; commands=() 168 | _describe -t commands 'tmplt init commands' commands "$@" 169 | } 170 | (( $+functions[_tmplt__help__list_commands] )) || 171 | _tmplt__help__list_commands() { 172 | local commands; commands=() 173 | _describe -t commands 'tmplt help list commands' commands "$@" 174 | } 175 | (( $+functions[_tmplt__list_commands] )) || 176 | _tmplt__list_commands() { 177 | local commands; commands=() 178 | _describe -t commands 'tmplt list commands' commands "$@" 179 | } 180 | (( $+functions[_tmplt__help__new_commands] )) || 181 | _tmplt__help__new_commands() { 182 | local commands; commands=() 183 | _describe -t commands 'tmplt help new commands' commands "$@" 184 | } 185 | (( $+functions[_tmplt__new_commands] )) || 186 | _tmplt__new_commands() { 187 | local commands; commands=() 188 | _describe -t commands 'tmplt new commands' commands "$@" 189 | } 190 | (( $+functions[_tmplt__help__remove_commands] )) || 191 | _tmplt__help__remove_commands() { 192 | local commands; commands=() 193 | _describe -t commands 'tmplt help remove commands' commands "$@" 194 | } 195 | (( $+functions[_tmplt__remove_commands] )) || 196 | _tmplt__remove_commands() { 197 | local commands; commands=() 198 | _describe -t commands 'tmplt remove commands' commands "$@" 199 | } 200 | (( $+functions[_tmplt__help__version_commands] )) || 201 | _tmplt__help__version_commands() { 202 | local commands; commands=() 203 | _describe -t commands 'tmplt help version commands' commands "$@" 204 | } 205 | (( $+functions[_tmplt__version_commands] )) || 206 | _tmplt__version_commands() { 207 | local commands; commands=() 208 | _describe -t commands 'tmplt version commands' commands "$@" 209 | } 210 | 211 | if [ "$funcstack[1]" = "_tmplt" ]; then 212 | _tmplt "$@" 213 | else 214 | compdef _tmplt tmplt 215 | fi 216 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::{fs::File, io::BufReader}; 3 | 4 | use inquire::Text; 5 | use serde_yaml::Value; 6 | 7 | use crate::error; 8 | 9 | pub struct Template { 10 | pub name: String, 11 | pub description: String, 12 | pub setup_commands: Option>, 13 | pub dependencies: Option>, 14 | pub files: Option>, 15 | pub post_setup_commands: Option>, 16 | } 17 | 18 | fn get_variable_value(name: &str, desc: &str, default_value: &str) -> String { 19 | let value = Text::new(&format!("Enter value for variable [{}]:", name)) 20 | .with_help_message(&format!( 21 | "{}\nPress enter to use default value: {}", 22 | desc, default_value, 23 | )) 24 | .with_default(default_value) 25 | .prompt(); 26 | 27 | if let Ok(value) = value { 28 | value.trim_matches('"').to_owned() 29 | } else { 30 | error!(format!("Failed to get value for variable: {}", name)); 31 | std::process::exit(1); 32 | } 33 | } 34 | 35 | pub fn parse(filepath: &str) -> Result> { 36 | let file = File::open(filepath)?; 37 | let reader = BufReader::new(file); 38 | 39 | let data: Value = serde_yaml::from_reader(reader)?; 40 | 41 | let mut files: Option>; 42 | 43 | files = if let Some(files_data) = data["files"].as_sequence() { 44 | let mut buffer = Vec::new(); 45 | 46 | for file_data in files_data { 47 | let file_data = file_data.as_mapping().ok_or("Expected mapping")?; 48 | let name = file_data["name"] 49 | .as_str() 50 | .ok_or(format!( 51 | "Expected file name [{:?}] as str", 52 | file_data["name"] 53 | ))? 54 | .to_owned(); 55 | 56 | let content = file_data["content"] 57 | .as_str() 58 | .ok_or(format!("Expected content for file [{}] as str", name))? 59 | .to_owned(); 60 | buffer.push((name, content)); 61 | } 62 | Some(buffer) 63 | } else { 64 | None 65 | }; 66 | 67 | let variables = if let Some(variables) = data["variables"].as_sequence() { 68 | Some( 69 | variables 70 | .iter() 71 | .map(|x| { 72 | let x = x.as_mapping().ok_or("Expected mapping")?; 73 | let name = x["name"].as_str().ok_or("Expected name as str")?.to_owned(); 74 | let default_value = serde_json::to_string(&x["default"]).unwrap_or_else(|_| { 75 | error!(format!( 76 | "Failed to parse default value for variable: {}", 77 | name 78 | )); 79 | std::process::exit(1); 80 | }); 81 | let description = x["description"] 82 | .as_str() 83 | .ok_or(format!( 84 | "Expected description [{:?}] as str", 85 | x["description"] 86 | ))? 87 | .to_owned(); 88 | Ok((name, description, default_value)) 89 | }) 90 | .collect::, Box>>()?, 91 | ) 92 | } else { 93 | None 94 | }; 95 | 96 | let mut name = data["name"] 97 | .as_str() 98 | .ok_or(format!("Expected name [{:?}] as str", data["name"]))? 99 | .to_owned(); 100 | 101 | let mut description = data["description"] 102 | .as_str() 103 | .ok_or(format!("Expected description for template {} as str", name))? 104 | .to_owned(); 105 | 106 | let mut setup_commands = if let Some(setup_commands) = data["setup_commands"].as_sequence() { 107 | Some( 108 | setup_commands 109 | .iter() 110 | .map(|x| Ok(x.as_str().ok_or("Expected str").unwrap().to_owned())) 111 | .collect::, Box>>()?, 112 | ) 113 | } else { 114 | None 115 | }; 116 | 117 | let mut dependencies = if let Some(dependencies) = data["dependencies"].as_sequence() { 118 | Some( 119 | dependencies 120 | .iter() 121 | .map(|x| { 122 | let x = x.as_mapping().ok_or("Expected mapping")?; 123 | let name = x["name"].as_str().ok_or("Expected name as str")?.to_owned(); 124 | let install_command = x["install_command"] 125 | .as_str() 126 | .ok_or("Expected install_command as str")? 127 | .to_owned(); 128 | Ok((name, install_command)) 129 | }) 130 | .collect::, Box>>()?, 131 | ) 132 | } else { 133 | None 134 | }; 135 | 136 | let mut post_setup_commands = 137 | if let Some(post_setup_commands) = data["post_setup_commands"].as_sequence() { 138 | Some( 139 | post_setup_commands 140 | .iter() 141 | .map(|x| Ok(x.as_str().ok_or("Expected str").unwrap().to_owned())) 142 | .collect::, Box>>()?, 143 | ) 144 | } else { 145 | None 146 | }; 147 | 148 | if let Some(variables) = variables { 149 | for (var_name, desc, default_value) in variables { 150 | let value = get_variable_value(&var_name, &desc, &default_value); 151 | 152 | name = name.replace(&format!("{{{}}}", var_name), &value); 153 | description = description.replace(&format!("{{{}}}", var_name), &value); 154 | 155 | if let Some(commands) = &mut setup_commands { 156 | for command in commands.iter_mut() { 157 | *command = command.replace(&format!("{{{}}}", var_name), &value); 158 | } 159 | } 160 | 161 | if let Some(dependencies) = &mut dependencies { 162 | for (name, install_command) in dependencies.iter_mut() { 163 | *name = name.replace(&format!("{{{}}}", var_name), &value); 164 | *install_command = 165 | install_command.replace(&format!("{{{}}}", var_name), &value); 166 | } 167 | }; 168 | 169 | if let Some(post_setup_commands) = &mut post_setup_commands { 170 | for command in post_setup_commands.iter_mut() { 171 | *command = command.replace(&format!("{{{}}}", var_name), &value); 172 | } 173 | } 174 | 175 | if let Some(files) = &mut files { 176 | for (name, content) in files.iter_mut() { 177 | *name = name.replace(&format!("{{{}}}", name), &value); 178 | *content = content.replace(&format!("{{{}}}", var_name), &value); 179 | } 180 | } 181 | } 182 | } 183 | 184 | Ok(Template { 185 | name, 186 | description, 187 | setup_commands, 188 | dependencies, 189 | files, 190 | post_setup_commands, 191 | }) 192 | } 193 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{create_dir_all, File}; 2 | use std::io::{BufRead, BufReader, BufWriter}; 3 | use std::path::PathBuf; 4 | use std::process::Command; 5 | 6 | use bincode::{deserialize_from, serialize_into}; 7 | use chrono::{Duration, Utc}; 8 | use colored::Colorize; 9 | use dirs::cache_dir; 10 | use semver::Version; 11 | use serde::{Deserialize, Serialize}; 12 | use tokio::runtime::Builder; 13 | 14 | use crate::error; 15 | 16 | /* 17 | * Cache file structure 18 | 19 | * version: String 20 | * last_update_time: DateTime 21 | */ 22 | #[derive(Serialize, Deserialize)] 23 | struct CacheData { 24 | version: String, 25 | last_update_time: chrono::DateTime, 26 | } 27 | 28 | /* 29 | * Get the cache directory and create it if it doesn't exist 30 | 31 | * @return Option - The cache directory 32 | */ 33 | fn get_cache_dir() -> Option { 34 | let app_name = env!("CARGO_PKG_NAME"); 35 | if let Some(cache_dir) = cache_dir() { 36 | let app_cache_dir = cache_dir.join(app_name); 37 | if !app_cache_dir.exists() { 38 | if let Err(e) = create_dir_all(&app_cache_dir) { 39 | error!(format!( 40 | "Failed to create cache directory {}: {}", 41 | app_cache_dir.display(), 42 | e 43 | )); 44 | return None; 45 | } 46 | } 47 | Some(app_cache_dir) 48 | } else { 49 | error!("Failed to get cache directory."); 50 | None 51 | } 52 | } 53 | 54 | /* 55 | * Get the latest version from the cache file, GitHub API or git 56 | * If the cache file doesn't exist, fetch the latest version from the Github API or git and create the cache file 57 | * If the cache file is older than 7 days, fetch the latest version from GitHub API 58 | * If the GitHub API fails, fetch the latest version from git 59 | 60 | * @return Version - The latest version 61 | */ 62 | pub fn get_latest_version() -> Version { 63 | let cache_dir = if let Some(cache_dir) = get_cache_dir() { 64 | cache_dir 65 | } else { 66 | println!("{}: Using 0.0.0 as fallback version", "Warning".yellow()); 67 | return Version::parse("0.1.0").unwrap(); 68 | }; 69 | 70 | let cache_file = cache_dir.join("cache.bin"); 71 | 72 | let cache_data: CacheData = match File::open(&cache_file) { 73 | Ok(file) => { 74 | let reader = BufReader::new(file); 75 | deserialize_from(reader).unwrap() 76 | } 77 | Err(_) => { 78 | let file = if let Ok(file) = File::create(&cache_file) { 79 | file 80 | } else { 81 | error!("Failed to create cache file"); 82 | println!("{}: Using 0.0.0 as fallback version", "Warning".yellow()); 83 | return Version::parse("0.0.0").unwrap(); 84 | }; 85 | 86 | let mut writer = BufWriter::new(file); 87 | let cache_data = CacheData { 88 | version: fetch_latest_version("0.0.0").to_string(), 89 | last_update_time: Utc::now(), 90 | }; 91 | 92 | serialize_into(&mut writer, &cache_data).unwrap(); 93 | 94 | cache_data 95 | } 96 | }; 97 | 98 | let seven_days_ago = Utc::now() - Duration::days(7); 99 | 100 | if cache_data.last_update_time <= seven_days_ago { 101 | let latest_version = fetch_latest_version(&cache_data.version); 102 | 103 | let mut new_cache_data = cache_data; 104 | new_cache_data.last_update_time = Utc::now(); 105 | new_cache_data.version = latest_version.to_string(); 106 | 107 | match File::create(&cache_file) { 108 | Ok(file) => { 109 | let mut writer = BufWriter::new(file); 110 | serialize_into(&mut writer, &new_cache_data).unwrap(); 111 | } 112 | Err(e) => { 113 | error!(format!("Failed to get cache file: {}", e)); 114 | println!("{}: Using 0.0.0 as fallback version", "Warning".yellow()); 115 | return Version::parse("0.0.0").unwrap(); 116 | } 117 | }; 118 | 119 | latest_version 120 | } else if let Ok(version) = Version::parse(&cache_data.version) { 121 | version 122 | } else { 123 | error!("Failed to parse latest version from cache"); 124 | println!("{}: Using 0.0.0 as fallback version", "Warning".yellow()); 125 | Version::parse("0.0.0").unwrap() 126 | } 127 | } 128 | 129 | /* 130 | * Fetch the latest version from GitHub API or git 131 | * If the GitHub API fails, fetch the latest version from git 132 | * If the git command fails, return the fallback version 133 | 134 | * @param fallback_version: &str - The fallback version 135 | * @return Version - The latest version 136 | */ 137 | fn fetch_latest_version(fallback_version: &str) -> Version { 138 | run_fetch_version_from_github_api().unwrap_or_else(|| { 139 | if let Some(val) = fetch_version_from_git() { 140 | val 141 | } else { 142 | println!("{}: Failed to get latest version", "Error".red()); 143 | println!("{}: Using 0.0.0 as fallback version", "Warning".yellow()); 144 | println!( 145 | "{}: You can still use tmplt but won't be notified about new versions!", 146 | "Warning".yellow() 147 | ); 148 | if let Ok(version) = Version::parse(fallback_version) { 149 | version 150 | } else { 151 | error!("Failed to parse fallback version using 0.0.0 as fallback version now"); 152 | Version::parse("0.0.0").unwrap() 153 | } 154 | } 155 | }) 156 | } 157 | 158 | /* 159 | * Run the async function fetch_version_from_github_api 160 | 161 | * @return Option - The latest version returned by the async function 162 | */ 163 | fn run_fetch_version_from_github_api() -> Option { 164 | let rt = if let Ok(val) = Builder::new_current_thread().enable_all().build() { 165 | val 166 | } else { 167 | return None; 168 | }; 169 | 170 | rt.block_on(fetch_version_from_github_api()) 171 | } 172 | 173 | /* 174 | * Fetch the latest version from GitHub API 175 | * If the GitHub API fails, return None 176 | 177 | * @return Option - The latest version 178 | */ 179 | async fn fetch_version_from_github_api() -> Option { 180 | let url = "https://api.github.com/repos/humblepenguinn/tmplt/releases/latest"; 181 | let client = reqwest::Client::new(); 182 | let res = if let Ok(val) = client.get(url).header("User-Agent", "tmplt").send().await { 183 | val 184 | } else { 185 | return None; 186 | }; 187 | 188 | match res.status() { 189 | reqwest::StatusCode::OK => { 190 | let body = if let Ok(val) = res.text().await { 191 | val 192 | } else { 193 | return None; 194 | }; 195 | 196 | if body.contains("tag_name") { 197 | let mut tag_name = body.split("tag_name").collect::>()[1] 198 | .split('\"') 199 | .collect::>()[2]; 200 | 201 | tag_name = tag_name.trim_start_matches('v'); 202 | let latest_version = if let Ok(val) = Version::parse(tag_name) { 203 | val 204 | } else { 205 | return None; 206 | }; 207 | 208 | return Some(latest_version); 209 | } 210 | 211 | None 212 | } 213 | 214 | _ => None, 215 | } 216 | } 217 | 218 | /* 219 | * Fetch the latest version from git 220 | * If the git command fails, return None 221 | 222 | * @return Option - The latest version 223 | */ 224 | fn fetch_version_from_git() -> Option { 225 | if Command::new("git").arg("--version").output().is_err() { 226 | error!("Git is not installed"); 227 | return None; 228 | } 229 | 230 | let owner = "humblepenguinn"; 231 | let repo = "tmplt"; 232 | let output = Command::new("git") 233 | .arg("ls-remote") 234 | .arg(format!("https://github.com/{}/{}.git", owner, repo)) 235 | .output() 236 | .unwrap(); 237 | let reader = BufReader::new(output.stdout.as_slice()); 238 | let mut latest_tag = None; 239 | 240 | for line in reader.lines().filter_map(|x| x.ok()) { 241 | let parts: Vec<_> = line.split('\t').collect(); 242 | if parts.len() != 2 { 243 | continue; 244 | } 245 | let (ref_name, _) = (parts[1], parts[0]); 246 | if ref_name.starts_with("refs/tags/") { 247 | let tag = ref_name.trim_start_matches("refs/tags/").to_owned(); 248 | latest_tag = 249 | latest_tag.map_or(Some(tag.clone()), |latest| Some(std::cmp::max(latest, tag))); 250 | } 251 | } 252 | 253 | if let Some(mut tag) = latest_tag { 254 | tag = tag.trim_start_matches('v').to_string(); 255 | if let Ok(version) = Version::parse(&tag) { 256 | return Some(version); 257 | } 258 | } 259 | 260 | None 261 | } 262 | -------------------------------------------------------------------------------- /completions/tmplt.bash: -------------------------------------------------------------------------------- 1 | _tmplt() { 2 | local i cur prev opts cmd 3 | COMPREPLY=() 4 | cur="${COMP_WORDS[COMP_CWORD]}" 5 | prev="${COMP_WORDS[COMP_CWORD-1]}" 6 | cmd="" 7 | opts="" 8 | 9 | for i in ${COMP_WORDS[@]} 10 | do 11 | case "${cmd},${i}" in 12 | ",$1") 13 | cmd="tmplt" 14 | ;; 15 | tmplt,help) 16 | cmd="tmplt__help" 17 | ;; 18 | tmplt,import) 19 | cmd="tmplt__import" 20 | ;; 21 | tmplt,init) 22 | cmd="tmplt__init" 23 | ;; 24 | tmplt,list) 25 | cmd="tmplt__list" 26 | ;; 27 | tmplt,new) 28 | cmd="tmplt__new" 29 | ;; 30 | tmplt,remove) 31 | cmd="tmplt__remove" 32 | ;; 33 | tmplt,version) 34 | cmd="tmplt__version" 35 | ;; 36 | tmplt__help,help) 37 | cmd="tmplt__help__help" 38 | ;; 39 | tmplt__help,import) 40 | cmd="tmplt__help__import" 41 | ;; 42 | tmplt__help,init) 43 | cmd="tmplt__help__init" 44 | ;; 45 | tmplt__help,list) 46 | cmd="tmplt__help__list" 47 | ;; 48 | tmplt__help,new) 49 | cmd="tmplt__help__new" 50 | ;; 51 | tmplt__help,remove) 52 | cmd="tmplt__help__remove" 53 | ;; 54 | tmplt__help,version) 55 | cmd="tmplt__help__version" 56 | ;; 57 | *) 58 | ;; 59 | esac 60 | done 61 | 62 | case "${cmd}" in 63 | tmplt) 64 | opts="-h --help new init import list remove version help" 65 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then 66 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 67 | return 0 68 | fi 69 | case "${prev}" in 70 | *) 71 | COMPREPLY=() 72 | ;; 73 | esac 74 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 75 | return 0 76 | ;; 77 | tmplt__help) 78 | opts="new init import list remove version help" 79 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 80 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 81 | return 0 82 | fi 83 | case "${prev}" in 84 | *) 85 | COMPREPLY=() 86 | ;; 87 | esac 88 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 89 | return 0 90 | ;; 91 | tmplt__help__help) 92 | opts="" 93 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 94 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 95 | return 0 96 | fi 97 | case "${prev}" in 98 | *) 99 | COMPREPLY=() 100 | ;; 101 | esac 102 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 103 | return 0 104 | ;; 105 | tmplt__help__import) 106 | opts="" 107 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 108 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 109 | return 0 110 | fi 111 | case "${prev}" in 112 | *) 113 | COMPREPLY=() 114 | ;; 115 | esac 116 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 117 | return 0 118 | ;; 119 | tmplt__help__init) 120 | opts="" 121 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 122 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 123 | return 0 124 | fi 125 | case "${prev}" in 126 | *) 127 | COMPREPLY=() 128 | ;; 129 | esac 130 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 131 | return 0 132 | ;; 133 | tmplt__help__list) 134 | opts="" 135 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 136 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 137 | return 0 138 | fi 139 | case "${prev}" in 140 | *) 141 | COMPREPLY=() 142 | ;; 143 | esac 144 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 145 | return 0 146 | ;; 147 | tmplt__help__new) 148 | opts="" 149 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 150 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 151 | return 0 152 | fi 153 | case "${prev}" in 154 | *) 155 | COMPREPLY=() 156 | ;; 157 | esac 158 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 159 | return 0 160 | ;; 161 | tmplt__help__remove) 162 | opts="" 163 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 164 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 165 | return 0 166 | fi 167 | case "${prev}" in 168 | *) 169 | COMPREPLY=() 170 | ;; 171 | esac 172 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 173 | return 0 174 | ;; 175 | tmplt__help__version) 176 | opts="" 177 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 178 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 179 | return 0 180 | fi 181 | case "${prev}" in 182 | *) 183 | COMPREPLY=() 184 | ;; 185 | esac 186 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 187 | return 0 188 | ;; 189 | tmplt__import) 190 | opts="-h --help [ARGS]..." 191 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 192 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 193 | return 0 194 | fi 195 | case "${prev}" in 196 | *) 197 | COMPREPLY=() 198 | ;; 199 | esac 200 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 201 | return 0 202 | ;; 203 | tmplt__init) 204 | opts="-h --help [ARGS]..." 205 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 206 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 207 | return 0 208 | fi 209 | case "${prev}" in 210 | *) 211 | COMPREPLY=() 212 | ;; 213 | esac 214 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 215 | return 0 216 | ;; 217 | tmplt__list) 218 | opts="-h --help" 219 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 220 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 221 | return 0 222 | fi 223 | case "${prev}" in 224 | *) 225 | COMPREPLY=() 226 | ;; 227 | esac 228 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 229 | return 0 230 | ;; 231 | tmplt__new) 232 | opts="-h --help [ARGS]..." 233 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 234 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 235 | return 0 236 | fi 237 | case "${prev}" in 238 | *) 239 | COMPREPLY=() 240 | ;; 241 | esac 242 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 243 | return 0 244 | ;; 245 | tmplt__remove) 246 | opts="-h --help [ARGS]..." 247 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 248 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 249 | return 0 250 | fi 251 | case "${prev}" in 252 | *) 253 | COMPREPLY=() 254 | ;; 255 | esac 256 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 257 | return 0 258 | ;; 259 | tmplt__version) 260 | opts="-h --help [ARGS]..." 261 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 262 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 263 | return 0 264 | fi 265 | case "${prev}" in 266 | *) 267 | COMPREPLY=() 268 | ;; 269 | esac 270 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 271 | return 0 272 | ;; 273 | esac 274 | } 275 | 276 | complete -F _tmplt -o bashdefault -o default tmplt 277 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2023 Humble Penguin 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /.github/workflows/CICD.yml: -------------------------------------------------------------------------------- 1 | name: CICD 2 | 3 | env: 4 | CICD_INTERMEDIATES_DIR: "_cicd-intermediates" 5 | 6 | on: 7 | pull_request: 8 | paths: 9 | - 'src/**' 10 | - '.github/**' 11 | - 'Cargo.toml' 12 | - 'Cargo.lock' 13 | - 'build.rs' 14 | push: 15 | branches: 16 | - main 17 | paths: 18 | - 'src/**' 19 | - '.github/**' 20 | - 'Cargo.toml' 21 | - 'Cargo.lock' 22 | - 'build.rs' 23 | tags: 24 | - "*" 25 | 26 | permissions: 27 | contents: write 28 | 29 | jobs: 30 | min_version: 31 | name: Minimum supported rust version 32 | runs-on: ubuntu-20.04 33 | steps: 34 | - name: Checkout source code 35 | uses: actions/checkout@v3 36 | - name: Get the MSRV from the package metadata 37 | id: msrv 38 | run: cargo metadata --no-deps --format-version 1 | jq -r '"version=" + (.packages[] | select(.name = "hyperfine").rust_version)' >> $GITHUB_OUTPUT 39 | - name: Install rust toolchain (v${{ steps.msrv.outputs.version }}) 40 | uses: dtolnay/rust-toolchain@master 41 | with: 42 | toolchain: ${{ steps.msrv.outputs.version }} 43 | components: clippy, rustfmt 44 | - name: Run cargo fmt 45 | run: cargo fmt -- --check 46 | - name: Run clippy (on minimum supported rust version to prevent warnings we can't fix) 47 | run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }} 48 | - name: Run tests 49 | run: cargo test --locked ${{ env.MSRV_FEATURES }} 50 | 51 | build: 52 | name: ${{ matrix.job.os }} (${{ matrix.job.target }}) 53 | runs-on: ${{ matrix.job.os }} 54 | strategy: 55 | fail-fast: false 56 | matrix: 57 | job: 58 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, use-cross: true } 59 | - { target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, use-cross: true } 60 | - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true } 61 | - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } 62 | - { target: i686-unknown-linux-gnu, os: ubuntu-20.04, use-cross: true } 63 | - { target: x86_64-apple-darwin, os: macos-12, } 64 | - { target: x86_64-pc-windows-gnu, os: windows-2019, arch: x86_64 } 65 | - { target: i686-pc-windows-msvc, os: windows-2019, arch: x86 } 66 | 67 | env: 68 | BUILD_CMD: cargo 69 | steps: 70 | - name: Checkout source code 71 | uses: actions/checkout@v3 72 | 73 | - name: Install prerequisites 74 | shell: bash 75 | run: | 76 | case ${{ matrix.job.target }} in 77 | arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; 78 | aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; 79 | esac 80 | 81 | - name: Extract crate information 82 | shell: bash 83 | run: | 84 | echo "PROJECT_NAME=tmplt" >> $GITHUB_ENV 85 | echo "PROJECT_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)" >> $GITHUB_ENV 86 | echo "PROJECT_MAINTAINER=$(sed -n 's/^authors = \["\(.*\)"\]/\1/p' Cargo.toml)" >> $GITHUB_ENV 87 | echo "PROJECT_HOMEPAGE=$(sed -n 's/^homepage = "\(.*\)"/\1/p' Cargo.toml)" >> $GITHUB_ENV 88 | 89 | - name: Install Rust toolchain 90 | uses: dtolnay/rust-toolchain@stable 91 | with: 92 | targets: ${{ matrix.job.target }} 93 | 94 | - name: Show version information (Rust, cargo, GCC) 95 | shell: bash 96 | run: | 97 | gcc --version || true 98 | rustup -V 99 | rustup toolchain list 100 | rustup default 101 | cargo -V 102 | rustc -V 103 | 104 | - name: Install cross 105 | if: matrix.job.use-cross 106 | uses: taiki-e/install-action@v2 107 | with: 108 | tool: cross 109 | 110 | - name: Overwrite build command env variable 111 | if: matrix.job.use-cross 112 | shell: bash 113 | run: | 114 | sudo apt-get install libudev-dev 115 | sudo apt-get install libssl-dev 116 | 117 | echo "BUILD_CMD=cross" >> $GITHUB_ENV 118 | 119 | - name: Build 120 | shell: bash 121 | run: | 122 | $BUILD_CMD build --locked --release --target=${{ matrix.job.target }} 123 | 124 | - name: Strip debug information from executable 125 | id: strip 126 | shell: bash 127 | run: | 128 | # Figure out suffix of binary 129 | EXE_suffix="" 130 | case ${{ matrix.job.target }} in 131 | *-pc-windows-*) EXE_suffix=".exe" ;; 132 | esac; 133 | 134 | STRIP="strip" 135 | case ${{ matrix.job.target }} in 136 | arm-unknown-linux-*) STRIP="arm-linux-gnueabihf-strip" ;; 137 | aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;; 138 | *-pc-windows-msvc) STRIP="" ;; 139 | esac; 140 | 141 | BIN_DIR="${{ env.CICD_INTERMEDIATES_DIR }}/stripped-release-bin/" 142 | mkdir -p "${BIN_DIR}" 143 | BIN_NAME="${{ env.PROJECT_NAME }}${EXE_suffix}" 144 | BIN_PATH="${BIN_DIR}/${BIN_NAME}" 145 | 146 | cp "target/${{ matrix.job.target }}/release/${BIN_NAME}" "${BIN_DIR}" 147 | 148 | # Also strip if possible 149 | if [ -n "${STRIP}" ]; then 150 | "${STRIP}" "${BIN_PATH}" 151 | fi 152 | 153 | echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT 154 | echo "BIN_NAME=${BIN_NAME}" >> $GITHUB_OUTPUT 155 | 156 | - name: Run tests 157 | shell: bash 158 | run: $BUILD_CMD test --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} 159 | 160 | - name: Create tarball 161 | id: package 162 | shell: bash 163 | run: | 164 | PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac; 165 | PKG_BASENAME=${PROJECT_NAME}-v${PROJECT_VERSION}-${{ matrix.job.target }} 166 | PKG_NAME=${PKG_BASENAME}${PKG_suffix} 167 | echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT 168 | 169 | PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package" 170 | ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/" 171 | mkdir -p "${ARCHIVE_DIR}" 172 | mkdir -p "${ARCHIVE_DIR}/autocomplete" 173 | 174 | cp "${{ steps.strip.outputs.BIN_PATH }}" "$ARCHIVE_DIR" 175 | 176 | cp 'man/${{ env.PROJECT_NAME }}.1' "$ARCHIVE_DIR" 177 | 178 | cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR" 179 | 180 | cp completions/'${{ env.PROJECT_NAME }}.bash' "$ARCHIVE_DIR/autocomplete/" 181 | cp completions/'${{ env.PROJECT_NAME }}.fish' "$ARCHIVE_DIR/autocomplete/" 182 | cp completions/'_${{ env.PROJECT_NAME }}' "$ARCHIVE_DIR/autocomplete/" 183 | cp completions/'_${{ env.PROJECT_NAME }}.ps1' "$ARCHIVE_DIR/autocomplete/" 184 | 185 | pushd "${PKG_STAGING}/" >/dev/null 186 | case ${{ matrix.job.target }} in 187 | *-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;; 188 | *) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;; 189 | esac; 190 | popd >/dev/null 191 | 192 | echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT 193 | 194 | - name: Create Debian package 195 | id: debian-package 196 | shell: bash 197 | if: startsWith(matrix.job.os, 'ubuntu') 198 | run: | 199 | COPYRIGHT_YEARS="2023 - "$(date "+%Y") 200 | DPKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/debian-package" 201 | DPKG_DIR="${DPKG_STAGING}/dpkg" 202 | mkdir -p "${DPKG_DIR}" 203 | DPKG_BASENAME=${PROJECT_NAME} 204 | DPKG_CONFLICTS=${PROJECT_NAME}-musl 205 | case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac; 206 | DPKG_VERSION=${PROJECT_VERSION} 207 | unset DPKG_ARCH 208 | case ${{ matrix.job.target }} in 209 | aarch64-*-linux-*) DPKG_ARCH=arm64 ;; 210 | arm-*-linux-*hf) DPKG_ARCH=armhf ;; 211 | i686-*-linux-*) DPKG_ARCH=i686 ;; 212 | x86_64-*-linux-*) DPKG_ARCH=amd64 ;; 213 | *) DPKG_ARCH=notset ;; 214 | esac; 215 | DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" 216 | echo "DPKG_NAME=${DPKG_NAME}" >> $GITHUB_OUTPUT 217 | # Binary 218 | install -Dm755 "${{ steps.strip.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.strip.outputs.BIN_NAME }}" 219 | # Man page 220 | install -Dm644 'man/${{ env.PROJECT_NAME }}.1' "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1" 221 | gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1" 222 | # Autocompletion files 223 | install -Dm644 completions/'${{ env.PROJECT_NAME }}.bash' "${DPKG_DIR}/usr/share/bash-completion/completions/${{ env.PROJECT_NAME }}" 224 | install -Dm644 completions/'${{ env.PROJECT_NAME }}.fish' "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ env.PROJECT_NAME }}.fish" 225 | install -Dm644 completions/'_${{ env.PROJECT_NAME }}' "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ env.PROJECT_NAME }}" 226 | # README and LICENSE 227 | install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md" 228 | install -Dm644 "LICENSE-MIT" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-MIT" 229 | install -Dm644 "LICENSE-APACHE" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-APACHE" 230 | install -Dm644 "CHANGELOG.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog" 231 | gzip -n --best "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog" 232 | cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" < "${DPKG_DIR}/DEBIAN/control" <> $GITHUB_OUTPUT 285 | # build dpkg 286 | fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}" 287 | 288 | - name: Set path for candle and light 289 | shell: bash 290 | if: startsWith(matrix.job.os, 'windows') 291 | run: echo "C:\Program Files (x86)\WiX Toolset v3.11\bin" >> $GITHUB_PATH 292 | 293 | - name: Build Windows Installer 294 | id: msi-installer 295 | shell: bash 296 | if: startsWith(matrix.job.os, 'windows') 297 | run: | 298 | cargo install cargo-wix 299 | cargo wix init 300 | cargo wix --target ${{ matrix.job.target }} 301 | 302 | PKG_NAME=${PROJECT_NAME}-${PROJECT_VERSION}-${{ matrix.job.arch }}.msi 303 | 304 | echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT 305 | echo "PKG_PATH=target/wix/${PKG_NAME}" >> $GITHUB_OUTPUT 306 | 307 | 308 | - name: "Artifact upload: tarball" 309 | uses: actions/upload-artifact@master 310 | with: 311 | name: ${{ steps.package.outputs.PKG_NAME }} 312 | path: ${{ steps.package.outputs.PKG_PATH }} 313 | 314 | - name: "Artifact upload: Windows installer" 315 | uses: actions/upload-artifact@master 316 | if: steps.msi-installer.outputs.PKG_NAME 317 | with: 318 | name: ${{ steps.msi-installer.outputs.PKG_NAME }} 319 | path: ${{ steps.msi-installer.outputs.PKG_PATH }} 320 | 321 | - name: "Artifact upload: Debian package" 322 | uses: actions/upload-artifact@master 323 | if: steps.debian-package.outputs.DPKG_NAME 324 | with: 325 | name: ${{ steps.debian-package.outputs.DPKG_NAME }} 326 | path: ${{ steps.debian-package.outputs.DPKG_PATH }} 327 | 328 | - name: Check for release 329 | id: is-release 330 | shell: bash 331 | run: | 332 | unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi 333 | echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT 334 | 335 | - name: Publish archives and packages 336 | uses: softprops/action-gh-release@v1 337 | if: steps.is-release.outputs.IS_RELEASE 338 | with: 339 | files: | 340 | ${{ steps.package.outputs.PKG_PATH }} 341 | ${{ steps.msi-installer.outputs.PKG_PATH }} 342 | ${{ steps.debian-package.outputs.DPKG_PATH }} 343 | env: 344 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 345 | -------------------------------------------------------------------------------- /src/command.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::io::Write; 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::mpsc::channel; 5 | use std::sync::Arc; 6 | use std::sync::Mutex; 7 | 8 | use colored::Colorize; 9 | use url::Url; 10 | 11 | use crate::cli::Command; 12 | use crate::error; 13 | use crate::parser::parse; 14 | use crate::progress_bar::{ 15 | get_progress_bar, get_progress_bar_handle, get_progress_bar_handle_with_rx, 16 | }; 17 | use crate::utils::{download_file, get_configdir}; 18 | 19 | impl Command { 20 | pub fn run(&self) { 21 | match self { 22 | Command::New(command_args) => { 23 | if command_args.args.len() != 2 { 24 | error!("Invalid number of arguments"); 25 | return; 26 | } 27 | 28 | let project_name = command_args.args[0].to_string(); 29 | 30 | let project_dir = std::path::PathBuf::from(project_name); 31 | if project_dir.exists() { 32 | error!("Directory with the same name already exists"); 33 | return; 34 | } 35 | 36 | std::fs::create_dir(&project_dir).unwrap(); 37 | 38 | if let Err(e) = std::env::set_current_dir(&project_dir) { 39 | error!(e.to_string()); 40 | std::process::exit(1); 41 | } 42 | 43 | let template_file_path = get_configdir() 44 | .join("templates") 45 | .join(command_args.args[1].to_string() + ".yaml"); 46 | 47 | if !(template_file_path.exists()) { 48 | error!("Template not found"); 49 | std::process::exit(1); 50 | } 51 | 52 | let template = parse(template_file_path.to_str().unwrap()).unwrap(); 53 | 54 | let done = Arc::new(AtomicBool::new(false)); 55 | let pb = Arc::new(Mutex::new(get_progress_bar())); 56 | 57 | fn create_execution_handle( 58 | command: Box, 59 | ) -> std::thread::JoinHandle<()> { 60 | std::thread::spawn(command) 61 | } 62 | 63 | fn run( 64 | execution_handle: std::thread::JoinHandle<()>, 65 | progress_bar_handle: std::thread::JoinHandle<()>, 66 | ) { 67 | let handles = vec![execution_handle, progress_bar_handle]; 68 | for handle in handles { 69 | handle.join().unwrap(); 70 | } 71 | } 72 | 73 | if let Some(setup_commands) = template.setup_commands { 74 | let done_clone = done.clone(); 75 | let pb_clone = pb.clone(); 76 | 77 | let (tx, rx) = channel(); 78 | run( 79 | create_execution_handle(Box::new(move || { 80 | for setup_command in setup_commands { 81 | let command = setup_command.split(' ').collect::>()[0]; 82 | let args = setup_command.split(' ').skip(1).collect::>(); 83 | 84 | if let Err(e) = tx.send(format!("Running Command: {}", command)) { 85 | error!(e.to_string()); 86 | std::process::exit(1); 87 | } 88 | 89 | let output = std::process::Command::new(command) 90 | .args(args) 91 | .output() 92 | .expect(command); 93 | 94 | if !output.stderr.is_empty() { 95 | error!(String::from_utf8_lossy(&output.stderr).to_string()); 96 | error!(command.to_string()); 97 | std::process::exit(1); 98 | } 99 | } 100 | done_clone.store(true, Ordering::SeqCst); 101 | })), 102 | get_progress_bar_handle_with_rx( 103 | done.clone(), 104 | pb_clone, 105 | "Running setup commands...".to_string(), 106 | rx, 107 | ), 108 | ); 109 | } 110 | 111 | if let Some(files) = template.files { 112 | let done_clone = done.clone(); 113 | let pb_clone = pb.clone(); 114 | 115 | run( 116 | create_execution_handle(Box::new(move || { 117 | for (filepath, buffer) in files { 118 | let path = std::path::Path::new(&filepath); 119 | let parent = path.parent(); 120 | 121 | if let Some(parent) = parent { 122 | if !parent.to_str().unwrap().is_empty() { 123 | std::fs::create_dir_all(parent).unwrap(); 124 | } 125 | } 126 | 127 | let mut file = if let Err(e) = std::fs::File::create(path) { 128 | error!(e.to_string()); 129 | std::process::exit(1); 130 | } else { 131 | std::fs::File::create(path).unwrap() 132 | }; 133 | 134 | if let Err(e) = file.write_all(buffer.as_bytes()) { 135 | error!(e.to_string()); 136 | std::process::exit(1); 137 | }; 138 | } 139 | done_clone.store(true, Ordering::SeqCst); 140 | })), 141 | get_progress_bar_handle( 142 | done.clone(), 143 | pb_clone, 144 | "Creating files...".to_string(), 145 | ), 146 | ); 147 | } 148 | 149 | if let Some(dependencies) = template.dependencies { 150 | let done_clone = done.clone(); 151 | let pb_clone = pb.clone(); 152 | 153 | let (tx, rx) = channel(); 154 | run( 155 | create_execution_handle(Box::new(move || { 156 | for (name, install_command) in dependencies { 157 | if let Err(e) = tx.send(format!("Installing {}", name.clone())) { 158 | error!(e.to_string()); 159 | std::process::exit(1); 160 | } 161 | 162 | let command = install_command.split(' ').collect::>()[0]; 163 | let args = 164 | install_command.split(' ').skip(1).collect::>(); 165 | 166 | let output = std::process::Command::new(command) 167 | .args(args) 168 | .output() 169 | .expect("failed to execute process"); 170 | 171 | if !output.status.success() { 172 | error!(String::from_utf8_lossy(&output.stderr).to_string()); 173 | 174 | std::process::exit(1); 175 | } 176 | } 177 | 178 | done_clone.store(true, Ordering::SeqCst); 179 | })), 180 | get_progress_bar_handle_with_rx( 181 | done.clone(), 182 | pb_clone, 183 | "Installing dependencies...".to_string(), 184 | rx, 185 | ), 186 | ); 187 | } 188 | 189 | if let Some(post_setup_commands) = template.post_setup_commands { 190 | let done_clone = done.clone(); 191 | let pb_clone = pb; 192 | 193 | let (tx, rx) = channel(); 194 | 195 | run( 196 | create_execution_handle(Box::new(move || { 197 | for post_setup_command in post_setup_commands { 198 | let command = 199 | post_setup_command.split(' ').collect::>()[0]; 200 | let args = 201 | post_setup_command.split(' ').skip(1).collect::>(); 202 | 203 | if let Err(e) = tx.send(format!("Running Command: {}", command)) { 204 | error!(e.to_string()); 205 | std::process::exit(1); 206 | } 207 | 208 | let output = std::process::Command::new(command) 209 | .args(args) 210 | .output() 211 | .expect("failed to execute process"); 212 | 213 | if !output.stderr.is_empty() { 214 | error!(String::from_utf8_lossy(&output.stderr).to_string()); 215 | std::process::exit(1); 216 | } 217 | } 218 | done_clone.store(true, Ordering::SeqCst); 219 | })), 220 | get_progress_bar_handle_with_rx( 221 | done, 222 | pb_clone, 223 | "Running post setup commands...".to_string(), 224 | rx, 225 | ), 226 | ); 227 | } 228 | 229 | println!( 230 | "{}: Ran Template {}", 231 | "Success".green(), 232 | command_args.args[1] 233 | ); 234 | } 235 | Command::Init(command_args) => { 236 | if command_args.args.is_empty() { 237 | error!("No Template name provided"); 238 | return; 239 | } 240 | 241 | let template_name = command_args.args[0].to_string(); 242 | 243 | let template_file_path = get_configdir() 244 | .join("templates") 245 | .join(template_name.clone() + ".yaml"); 246 | 247 | if !(template_file_path.exists()) { 248 | let mut file = if let Err(e) = std::fs::File::create(&template_file_path) { 249 | error!(e.to_string()); 250 | std::process::exit(1); 251 | } else { 252 | std::fs::File::create(&template_file_path).unwrap() 253 | }; 254 | 255 | let buffer = format!( 256 | " 257 | # Go to the GitHub repo https://github.com/humblepenguinn/tmplt for more information 258 | 259 | # Template information 260 | name: {} 261 | description: Description of the template 262 | 263 | # Commands to run during setup 264 | setup_commands: 265 | - command_1 266 | - command_2 267 | 268 | # Dependency information 269 | dependencies: 270 | - name: dependency_1 271 | install_command: install_command_1 272 | 273 | - name: dependency_2 274 | install_command: install_command_2 275 | 276 | # Files to generate 277 | files: 278 | - name: file_name_1 279 | content: | 280 | file contents 281 | - name: file_name_2 282 | content: | 283 | file contents 284 | 285 | # Post-setup command to run after setup is complete 286 | post_setup_commands: 287 | - command_1 288 | - command_2 289 | 290 | variables: 291 | - name: my_variable_name 292 | description: description of the variable 293 | default: the default value of the variable 294 | 295 | ", 296 | &template_name 297 | ); 298 | file.write_all(buffer.as_bytes()).unwrap(); 299 | println!( 300 | "{}: {}", 301 | "Success".green(), 302 | "Template created you can go edit it at ".to_string() 303 | + template_file_path.to_str().unwrap() 304 | ); 305 | } else { 306 | error!("Template already exists"); 307 | } 308 | } 309 | Command::Import(command_args) => { 310 | if command_args.args.len() < 2 { 311 | error!("Invalid number of arguments"); 312 | return; 313 | } 314 | 315 | let url = &command_args.args[0]; 316 | if Url::parse(command_args.args[0].as_str()).is_err() { 317 | error!("Invalid URL"); 318 | return; 319 | } 320 | 321 | let file_to_save_as = command_args.args[1].to_string(); 322 | 323 | let template_file_path = get_configdir() 324 | .join("templates") 325 | .join(file_to_save_as + ".yaml"); 326 | 327 | if template_file_path.exists() { 328 | error!("Template with the same name already exists"); 329 | return; 330 | } 331 | 332 | tokio::runtime::Builder::new_current_thread() 333 | .enable_all() 334 | .build() 335 | .unwrap_or_else(|e| { 336 | error!(e.to_string()); 337 | std::process::exit(1); 338 | }) 339 | .block_on(download_file(url, template_file_path.to_str().unwrap())); 340 | } 341 | 342 | Command::List => { 343 | let templates_dir = get_configdir().join("templates"); 344 | 345 | println!("{}", "Templates:".bold()); 346 | for entry in std::fs::read_dir(templates_dir).unwrap() { 347 | let entry = entry.unwrap(); 348 | let path = entry.path(); 349 | let filename = path.file_name().unwrap().to_str().unwrap().to_owned(); 350 | 351 | println!("{}", filename); 352 | } 353 | } 354 | Command::Remove(command_args) => { 355 | if command_args.args.is_empty() { 356 | error!("No Template name provided"); 357 | return; 358 | } 359 | 360 | let template_name = command_args.args[0].to_string(); 361 | 362 | let template_file_path = get_configdir() 363 | .join("templates") 364 | .join(template_name + ".yaml"); 365 | 366 | if template_file_path.exists() { 367 | std::fs::remove_file(template_file_path).unwrap(); 368 | println!("{}: Template removed", "Success".green()); 369 | } else { 370 | error!("Template does not exist"); 371 | } 372 | } 373 | 374 | Command::Version(command_args) => { 375 | if command_args.args.is_empty() { 376 | println!("{} {}", "Version".green(), env!("BUILD_VERSION")); 377 | } else if command_args.args[0] == "verbose" { 378 | println!("{} {}", "Version".green(), env!("BUILD_VERSION")); 379 | println!("{} {}", "Build Timestamp".green(), env!("BUILD_TIMESTAMP")); 380 | println!("{} {}", "Author".green(), env!("CARGO_PKG_AUTHORS")); 381 | println!("{} {}", "License".green(), env!("CARGO_PKG_LICENSE")); 382 | println!("{} {}", "Repository".green(), env!("CARGO_PKG_REPOSITORY")); 383 | } else { 384 | error!("Invalid argument"); 385 | } 386 | } 387 | } 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "android_system_properties" 7 | version = "0.1.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 10 | dependencies = [ 11 | "libc", 12 | ] 13 | 14 | [[package]] 15 | name = "anstream" 16 | version = "0.2.6" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" 19 | dependencies = [ 20 | "anstyle", 21 | "anstyle-parse", 22 | "anstyle-wincon", 23 | "concolor-override", 24 | "concolor-query", 25 | "is-terminal", 26 | "utf8parse", 27 | ] 28 | 29 | [[package]] 30 | name = "anstyle" 31 | version = "0.3.5" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" 34 | 35 | [[package]] 36 | name = "anstyle-parse" 37 | version = "0.1.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" 40 | dependencies = [ 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "0.2.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" 49 | dependencies = [ 50 | "anstyle", 51 | "windows-sys 0.45.0", 52 | ] 53 | 54 | [[package]] 55 | name = "atty" 56 | version = "0.2.14" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 59 | dependencies = [ 60 | "hermit-abi 0.1.19", 61 | "libc", 62 | "winapi", 63 | ] 64 | 65 | [[package]] 66 | name = "autocfg" 67 | version = "1.1.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 70 | 71 | [[package]] 72 | name = "base64" 73 | version = "0.21.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" 76 | 77 | [[package]] 78 | name = "bincode" 79 | version = "1.3.3" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 82 | dependencies = [ 83 | "serde", 84 | ] 85 | 86 | [[package]] 87 | name = "bitflags" 88 | version = "1.3.2" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 91 | 92 | [[package]] 93 | name = "bumpalo" 94 | version = "3.12.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" 97 | 98 | [[package]] 99 | name = "bytes" 100 | version = "1.4.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 103 | 104 | [[package]] 105 | name = "cc" 106 | version = "1.0.79" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 109 | 110 | [[package]] 111 | name = "cfg-if" 112 | version = "1.0.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 115 | 116 | [[package]] 117 | name = "chrono" 118 | version = "0.4.24" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" 121 | dependencies = [ 122 | "iana-time-zone", 123 | "js-sys", 124 | "num-integer", 125 | "num-traits", 126 | "serde", 127 | "time", 128 | "wasm-bindgen", 129 | "winapi", 130 | ] 131 | 132 | [[package]] 133 | name = "clap" 134 | version = "4.2.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" 137 | dependencies = [ 138 | "clap_builder", 139 | "clap_derive", 140 | "once_cell", 141 | ] 142 | 143 | [[package]] 144 | name = "clap_builder" 145 | version = "4.2.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" 148 | dependencies = [ 149 | "anstream", 150 | "anstyle", 151 | "bitflags", 152 | "clap_lex", 153 | "strsim", 154 | ] 155 | 156 | [[package]] 157 | name = "clap_complete" 158 | version = "4.2.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "01c22dcfb410883764b29953103d9ef7bb8fe21b3fa1158bc99986c2067294bd" 161 | dependencies = [ 162 | "clap", 163 | ] 164 | 165 | [[package]] 166 | name = "clap_derive" 167 | version = "4.2.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" 170 | dependencies = [ 171 | "heck", 172 | "proc-macro2", 173 | "quote", 174 | "syn 2.0.13", 175 | ] 176 | 177 | [[package]] 178 | name = "clap_lex" 179 | version = "0.4.1" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" 182 | 183 | [[package]] 184 | name = "clap_mangen" 185 | version = "0.2.10" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "4237e29de9c6949982ba87d51709204504fb8ed2fd38232fcb1e5bf7d4ba48c8" 188 | dependencies = [ 189 | "clap", 190 | "roff", 191 | ] 192 | 193 | [[package]] 194 | name = "codespan-reporting" 195 | version = "0.11.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 198 | dependencies = [ 199 | "termcolor", 200 | "unicode-width", 201 | ] 202 | 203 | [[package]] 204 | name = "colored" 205 | version = "2.0.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 208 | dependencies = [ 209 | "atty", 210 | "lazy_static", 211 | "winapi", 212 | ] 213 | 214 | [[package]] 215 | name = "concolor-override" 216 | version = "1.0.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" 219 | 220 | [[package]] 221 | name = "concolor-query" 222 | version = "0.3.3" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" 225 | dependencies = [ 226 | "windows-sys 0.45.0", 227 | ] 228 | 229 | [[package]] 230 | name = "console" 231 | version = "0.15.5" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" 234 | dependencies = [ 235 | "encode_unicode", 236 | "lazy_static", 237 | "libc", 238 | "unicode-width", 239 | "windows-sys 0.42.0", 240 | ] 241 | 242 | [[package]] 243 | name = "core-foundation" 244 | version = "0.9.3" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 247 | dependencies = [ 248 | "core-foundation-sys", 249 | "libc", 250 | ] 251 | 252 | [[package]] 253 | name = "core-foundation-sys" 254 | version = "0.8.4" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 257 | 258 | [[package]] 259 | name = "crossterm" 260 | version = "0.25.0" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" 263 | dependencies = [ 264 | "bitflags", 265 | "crossterm_winapi", 266 | "libc", 267 | "mio", 268 | "parking_lot", 269 | "signal-hook", 270 | "signal-hook-mio", 271 | "winapi", 272 | ] 273 | 274 | [[package]] 275 | name = "crossterm_winapi" 276 | version = "0.9.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" 279 | dependencies = [ 280 | "winapi", 281 | ] 282 | 283 | [[package]] 284 | name = "cxx" 285 | version = "1.0.94" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" 288 | dependencies = [ 289 | "cc", 290 | "cxxbridge-flags", 291 | "cxxbridge-macro", 292 | "link-cplusplus", 293 | ] 294 | 295 | [[package]] 296 | name = "cxx-build" 297 | version = "1.0.94" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" 300 | dependencies = [ 301 | "cc", 302 | "codespan-reporting", 303 | "once_cell", 304 | "proc-macro2", 305 | "quote", 306 | "scratch", 307 | "syn 2.0.13", 308 | ] 309 | 310 | [[package]] 311 | name = "cxxbridge-flags" 312 | version = "1.0.94" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" 315 | 316 | [[package]] 317 | name = "cxxbridge-macro" 318 | version = "1.0.94" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" 321 | dependencies = [ 322 | "proc-macro2", 323 | "quote", 324 | "syn 2.0.13", 325 | ] 326 | 327 | [[package]] 328 | name = "dirs" 329 | version = "5.0.0" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd" 332 | dependencies = [ 333 | "dirs-sys", 334 | ] 335 | 336 | [[package]] 337 | name = "dirs-sys" 338 | version = "0.4.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" 341 | dependencies = [ 342 | "libc", 343 | "redox_users", 344 | "windows-sys 0.45.0", 345 | ] 346 | 347 | [[package]] 348 | name = "dyn-clone" 349 | version = "1.0.11" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" 352 | 353 | [[package]] 354 | name = "encode_unicode" 355 | version = "0.3.6" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 358 | 359 | [[package]] 360 | name = "encoding_rs" 361 | version = "0.8.32" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" 364 | dependencies = [ 365 | "cfg-if", 366 | ] 367 | 368 | [[package]] 369 | name = "errno" 370 | version = "0.3.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" 373 | dependencies = [ 374 | "errno-dragonfly", 375 | "libc", 376 | "windows-sys 0.45.0", 377 | ] 378 | 379 | [[package]] 380 | name = "errno-dragonfly" 381 | version = "0.1.2" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 384 | dependencies = [ 385 | "cc", 386 | "libc", 387 | ] 388 | 389 | [[package]] 390 | name = "fnv" 391 | version = "1.0.7" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 394 | 395 | [[package]] 396 | name = "form_urlencoded" 397 | version = "1.1.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 400 | dependencies = [ 401 | "percent-encoding", 402 | ] 403 | 404 | [[package]] 405 | name = "futures-channel" 406 | version = "0.3.28" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" 409 | dependencies = [ 410 | "futures-core", 411 | ] 412 | 413 | [[package]] 414 | name = "futures-core" 415 | version = "0.3.28" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 418 | 419 | [[package]] 420 | name = "futures-sink" 421 | version = "0.3.28" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" 424 | 425 | [[package]] 426 | name = "futures-task" 427 | version = "0.3.28" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" 430 | 431 | [[package]] 432 | name = "futures-util" 433 | version = "0.3.28" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" 436 | dependencies = [ 437 | "futures-core", 438 | "futures-task", 439 | "pin-project-lite", 440 | "pin-utils", 441 | ] 442 | 443 | [[package]] 444 | name = "getrandom" 445 | version = "0.2.8" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 448 | dependencies = [ 449 | "cfg-if", 450 | "libc", 451 | "wasi 0.11.0+wasi-snapshot-preview1", 452 | ] 453 | 454 | [[package]] 455 | name = "h2" 456 | version = "0.3.16" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" 459 | dependencies = [ 460 | "bytes", 461 | "fnv", 462 | "futures-core", 463 | "futures-sink", 464 | "futures-util", 465 | "http", 466 | "indexmap", 467 | "slab", 468 | "tokio", 469 | "tokio-util", 470 | "tracing", 471 | ] 472 | 473 | [[package]] 474 | name = "hashbrown" 475 | version = "0.12.3" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 478 | 479 | [[package]] 480 | name = "heck" 481 | version = "0.4.1" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 484 | 485 | [[package]] 486 | name = "hermit-abi" 487 | version = "0.1.19" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 490 | dependencies = [ 491 | "libc", 492 | ] 493 | 494 | [[package]] 495 | name = "hermit-abi" 496 | version = "0.3.1" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 499 | 500 | [[package]] 501 | name = "http" 502 | version = "0.2.9" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" 505 | dependencies = [ 506 | "bytes", 507 | "fnv", 508 | "itoa", 509 | ] 510 | 511 | [[package]] 512 | name = "http-body" 513 | version = "0.4.5" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 516 | dependencies = [ 517 | "bytes", 518 | "http", 519 | "pin-project-lite", 520 | ] 521 | 522 | [[package]] 523 | name = "httparse" 524 | version = "1.8.0" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 527 | 528 | [[package]] 529 | name = "httpdate" 530 | version = "1.0.2" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 533 | 534 | [[package]] 535 | name = "hyper" 536 | version = "0.14.25" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" 539 | dependencies = [ 540 | "bytes", 541 | "futures-channel", 542 | "futures-core", 543 | "futures-util", 544 | "h2", 545 | "http", 546 | "http-body", 547 | "httparse", 548 | "httpdate", 549 | "itoa", 550 | "pin-project-lite", 551 | "socket2", 552 | "tokio", 553 | "tower-service", 554 | "tracing", 555 | "want", 556 | ] 557 | 558 | [[package]] 559 | name = "hyper-rustls" 560 | version = "0.23.2" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" 563 | dependencies = [ 564 | "http", 565 | "hyper", 566 | "rustls", 567 | "tokio", 568 | "tokio-rustls", 569 | ] 570 | 571 | [[package]] 572 | name = "iana-time-zone" 573 | version = "0.1.56" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" 576 | dependencies = [ 577 | "android_system_properties", 578 | "core-foundation-sys", 579 | "iana-time-zone-haiku", 580 | "js-sys", 581 | "wasm-bindgen", 582 | "windows", 583 | ] 584 | 585 | [[package]] 586 | name = "iana-time-zone-haiku" 587 | version = "0.1.1" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" 590 | dependencies = [ 591 | "cxx", 592 | "cxx-build", 593 | ] 594 | 595 | [[package]] 596 | name = "idna" 597 | version = "0.3.0" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 600 | dependencies = [ 601 | "unicode-bidi", 602 | "unicode-normalization", 603 | ] 604 | 605 | [[package]] 606 | name = "indexmap" 607 | version = "1.9.3" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 610 | dependencies = [ 611 | "autocfg", 612 | "hashbrown", 613 | ] 614 | 615 | [[package]] 616 | name = "indicatif" 617 | version = "0.17.3" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" 620 | dependencies = [ 621 | "console", 622 | "number_prefix", 623 | "portable-atomic", 624 | "unicode-width", 625 | ] 626 | 627 | [[package]] 628 | name = "inquire" 629 | version = "0.6.0" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "fd079157ad94a32f7511b2e13037f3ae417ad80a6a9b0de29154d48b86f5d6c8" 632 | dependencies = [ 633 | "bitflags", 634 | "crossterm", 635 | "dyn-clone", 636 | "lazy_static", 637 | "newline-converter", 638 | "thiserror", 639 | "unicode-segmentation", 640 | "unicode-width", 641 | ] 642 | 643 | [[package]] 644 | name = "io-lifetimes" 645 | version = "1.0.9" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" 648 | dependencies = [ 649 | "hermit-abi 0.3.1", 650 | "libc", 651 | "windows-sys 0.45.0", 652 | ] 653 | 654 | [[package]] 655 | name = "ipnet" 656 | version = "2.7.2" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" 659 | 660 | [[package]] 661 | name = "is-terminal" 662 | version = "0.4.6" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" 665 | dependencies = [ 666 | "hermit-abi 0.3.1", 667 | "io-lifetimes", 668 | "rustix", 669 | "windows-sys 0.45.0", 670 | ] 671 | 672 | [[package]] 673 | name = "itoa" 674 | version = "1.0.6" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 677 | 678 | [[package]] 679 | name = "js-sys" 680 | version = "0.3.61" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 683 | dependencies = [ 684 | "wasm-bindgen", 685 | ] 686 | 687 | [[package]] 688 | name = "lazy_static" 689 | version = "1.4.0" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 692 | 693 | [[package]] 694 | name = "libc" 695 | version = "0.2.140" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" 698 | 699 | [[package]] 700 | name = "link-cplusplus" 701 | version = "1.0.8" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" 704 | dependencies = [ 705 | "cc", 706 | ] 707 | 708 | [[package]] 709 | name = "linux-raw-sys" 710 | version = "0.3.1" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" 713 | 714 | [[package]] 715 | name = "lock_api" 716 | version = "0.4.9" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 719 | dependencies = [ 720 | "autocfg", 721 | "scopeguard", 722 | ] 723 | 724 | [[package]] 725 | name = "log" 726 | version = "0.4.17" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 729 | dependencies = [ 730 | "cfg-if", 731 | ] 732 | 733 | [[package]] 734 | name = "mime" 735 | version = "0.3.17" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 738 | 739 | [[package]] 740 | name = "mio" 741 | version = "0.8.6" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" 744 | dependencies = [ 745 | "libc", 746 | "log", 747 | "wasi 0.11.0+wasi-snapshot-preview1", 748 | "windows-sys 0.45.0", 749 | ] 750 | 751 | [[package]] 752 | name = "newline-converter" 753 | version = "0.2.2" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" 756 | dependencies = [ 757 | "unicode-segmentation", 758 | ] 759 | 760 | [[package]] 761 | name = "num-integer" 762 | version = "0.1.45" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 765 | dependencies = [ 766 | "autocfg", 767 | "num-traits", 768 | ] 769 | 770 | [[package]] 771 | name = "num-traits" 772 | version = "0.2.15" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 775 | dependencies = [ 776 | "autocfg", 777 | ] 778 | 779 | [[package]] 780 | name = "number_prefix" 781 | version = "0.4.0" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 784 | 785 | [[package]] 786 | name = "once_cell" 787 | version = "1.17.1" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 790 | 791 | [[package]] 792 | name = "openssl-probe" 793 | version = "0.1.5" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 796 | 797 | [[package]] 798 | name = "parking_lot" 799 | version = "0.12.1" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 802 | dependencies = [ 803 | "lock_api", 804 | "parking_lot_core", 805 | ] 806 | 807 | [[package]] 808 | name = "parking_lot_core" 809 | version = "0.9.7" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" 812 | dependencies = [ 813 | "cfg-if", 814 | "libc", 815 | "redox_syscall", 816 | "smallvec", 817 | "windows-sys 0.45.0", 818 | ] 819 | 820 | [[package]] 821 | name = "percent-encoding" 822 | version = "2.2.0" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 825 | 826 | [[package]] 827 | name = "pin-project-lite" 828 | version = "0.2.9" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 831 | 832 | [[package]] 833 | name = "pin-utils" 834 | version = "0.1.0" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 837 | 838 | [[package]] 839 | name = "portable-atomic" 840 | version = "0.3.19" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" 843 | 844 | [[package]] 845 | name = "proc-macro2" 846 | version = "1.0.56" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 849 | dependencies = [ 850 | "unicode-ident", 851 | ] 852 | 853 | [[package]] 854 | name = "quote" 855 | version = "1.0.26" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 858 | dependencies = [ 859 | "proc-macro2", 860 | ] 861 | 862 | [[package]] 863 | name = "redox_syscall" 864 | version = "0.2.16" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 867 | dependencies = [ 868 | "bitflags", 869 | ] 870 | 871 | [[package]] 872 | name = "redox_users" 873 | version = "0.4.3" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" 876 | dependencies = [ 877 | "getrandom", 878 | "redox_syscall", 879 | "thiserror", 880 | ] 881 | 882 | [[package]] 883 | name = "reqwest" 884 | version = "0.11.16" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" 887 | dependencies = [ 888 | "base64", 889 | "bytes", 890 | "encoding_rs", 891 | "futures-core", 892 | "futures-util", 893 | "h2", 894 | "http", 895 | "http-body", 896 | "hyper", 897 | "hyper-rustls", 898 | "ipnet", 899 | "js-sys", 900 | "log", 901 | "mime", 902 | "once_cell", 903 | "percent-encoding", 904 | "pin-project-lite", 905 | "rustls", 906 | "rustls-native-certs", 907 | "rustls-pemfile", 908 | "serde", 909 | "serde_json", 910 | "serde_urlencoded", 911 | "tokio", 912 | "tokio-rustls", 913 | "tower-service", 914 | "url", 915 | "wasm-bindgen", 916 | "wasm-bindgen-futures", 917 | "web-sys", 918 | "winreg", 919 | ] 920 | 921 | [[package]] 922 | name = "ring" 923 | version = "0.16.20" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 926 | dependencies = [ 927 | "cc", 928 | "libc", 929 | "once_cell", 930 | "spin", 931 | "untrusted", 932 | "web-sys", 933 | "winapi", 934 | ] 935 | 936 | [[package]] 937 | name = "roff" 938 | version = "0.2.1" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" 941 | 942 | [[package]] 943 | name = "rustix" 944 | version = "0.37.6" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849" 947 | dependencies = [ 948 | "bitflags", 949 | "errno", 950 | "io-lifetimes", 951 | "libc", 952 | "linux-raw-sys", 953 | "windows-sys 0.45.0", 954 | ] 955 | 956 | [[package]] 957 | name = "rustls" 958 | version = "0.20.8" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" 961 | dependencies = [ 962 | "log", 963 | "ring", 964 | "sct", 965 | "webpki", 966 | ] 967 | 968 | [[package]] 969 | name = "rustls-native-certs" 970 | version = "0.6.2" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" 973 | dependencies = [ 974 | "openssl-probe", 975 | "rustls-pemfile", 976 | "schannel", 977 | "security-framework", 978 | ] 979 | 980 | [[package]] 981 | name = "rustls-pemfile" 982 | version = "1.0.2" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" 985 | dependencies = [ 986 | "base64", 987 | ] 988 | 989 | [[package]] 990 | name = "ryu" 991 | version = "1.0.13" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 994 | 995 | [[package]] 996 | name = "schannel" 997 | version = "0.1.21" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" 1000 | dependencies = [ 1001 | "windows-sys 0.42.0", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "scopeguard" 1006 | version = "1.1.0" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1009 | 1010 | [[package]] 1011 | name = "scratch" 1012 | version = "1.0.5" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" 1015 | 1016 | [[package]] 1017 | name = "sct" 1018 | version = "0.7.0" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 1021 | dependencies = [ 1022 | "ring", 1023 | "untrusted", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "security-framework" 1028 | version = "2.8.2" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" 1031 | dependencies = [ 1032 | "bitflags", 1033 | "core-foundation", 1034 | "core-foundation-sys", 1035 | "libc", 1036 | "security-framework-sys", 1037 | ] 1038 | 1039 | [[package]] 1040 | name = "security-framework-sys" 1041 | version = "2.8.0" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" 1044 | dependencies = [ 1045 | "core-foundation-sys", 1046 | "libc", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "semver" 1051 | version = "1.0.17" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" 1054 | 1055 | [[package]] 1056 | name = "serde" 1057 | version = "1.0.159" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" 1060 | dependencies = [ 1061 | "serde_derive", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "serde_derive" 1066 | version = "1.0.159" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" 1069 | dependencies = [ 1070 | "proc-macro2", 1071 | "quote", 1072 | "syn 2.0.13", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "serde_json" 1077 | version = "1.0.95" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" 1080 | dependencies = [ 1081 | "itoa", 1082 | "ryu", 1083 | "serde", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "serde_urlencoded" 1088 | version = "0.7.1" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1091 | dependencies = [ 1092 | "form_urlencoded", 1093 | "itoa", 1094 | "ryu", 1095 | "serde", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "serde_yaml" 1100 | version = "0.9.19" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" 1103 | dependencies = [ 1104 | "indexmap", 1105 | "itoa", 1106 | "ryu", 1107 | "serde", 1108 | "unsafe-libyaml", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "signal-hook" 1113 | version = "0.3.15" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" 1116 | dependencies = [ 1117 | "libc", 1118 | "signal-hook-registry", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "signal-hook-mio" 1123 | version = "0.2.3" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 1126 | dependencies = [ 1127 | "libc", 1128 | "mio", 1129 | "signal-hook", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "signal-hook-registry" 1134 | version = "1.4.1" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 1137 | dependencies = [ 1138 | "libc", 1139 | ] 1140 | 1141 | [[package]] 1142 | name = "slab" 1143 | version = "0.4.8" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 1146 | dependencies = [ 1147 | "autocfg", 1148 | ] 1149 | 1150 | [[package]] 1151 | name = "smallvec" 1152 | version = "1.10.0" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1155 | 1156 | [[package]] 1157 | name = "socket2" 1158 | version = "0.4.9" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 1161 | dependencies = [ 1162 | "libc", 1163 | "winapi", 1164 | ] 1165 | 1166 | [[package]] 1167 | name = "spin" 1168 | version = "0.5.2" 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" 1170 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1171 | 1172 | [[package]] 1173 | name = "strsim" 1174 | version = "0.10.0" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1177 | 1178 | [[package]] 1179 | name = "syn" 1180 | version = "1.0.109" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1183 | dependencies = [ 1184 | "proc-macro2", 1185 | "quote", 1186 | "unicode-ident", 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "syn" 1191 | version = "2.0.13" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" 1194 | dependencies = [ 1195 | "proc-macro2", 1196 | "quote", 1197 | "unicode-ident", 1198 | ] 1199 | 1200 | [[package]] 1201 | name = "termcolor" 1202 | version = "1.2.0" 1203 | source = "registry+https://github.com/rust-lang/crates.io-index" 1204 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 1205 | dependencies = [ 1206 | "winapi-util", 1207 | ] 1208 | 1209 | [[package]] 1210 | name = "thiserror" 1211 | version = "1.0.40" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" 1214 | dependencies = [ 1215 | "thiserror-impl", 1216 | ] 1217 | 1218 | [[package]] 1219 | name = "thiserror-impl" 1220 | version = "1.0.40" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" 1223 | dependencies = [ 1224 | "proc-macro2", 1225 | "quote", 1226 | "syn 2.0.13", 1227 | ] 1228 | 1229 | [[package]] 1230 | name = "time" 1231 | version = "0.1.45" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" 1234 | dependencies = [ 1235 | "libc", 1236 | "wasi 0.10.0+wasi-snapshot-preview1", 1237 | "winapi", 1238 | ] 1239 | 1240 | [[package]] 1241 | name = "tinyvec" 1242 | version = "1.6.0" 1243 | source = "registry+https://github.com/rust-lang/crates.io-index" 1244 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1245 | dependencies = [ 1246 | "tinyvec_macros", 1247 | ] 1248 | 1249 | [[package]] 1250 | name = "tinyvec_macros" 1251 | version = "0.1.1" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1254 | 1255 | [[package]] 1256 | name = "tmplt" 1257 | version = "0.1.1" 1258 | dependencies = [ 1259 | "bincode", 1260 | "chrono", 1261 | "clap", 1262 | "clap_complete", 1263 | "clap_mangen", 1264 | "colored", 1265 | "dirs", 1266 | "indicatif", 1267 | "inquire", 1268 | "reqwest", 1269 | "semver", 1270 | "serde", 1271 | "serde_json", 1272 | "serde_yaml", 1273 | "tokio", 1274 | "url", 1275 | ] 1276 | 1277 | [[package]] 1278 | name = "tokio" 1279 | version = "1.27.0" 1280 | source = "registry+https://github.com/rust-lang/crates.io-index" 1281 | checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" 1282 | dependencies = [ 1283 | "autocfg", 1284 | "bytes", 1285 | "libc", 1286 | "mio", 1287 | "pin-project-lite", 1288 | "socket2", 1289 | "windows-sys 0.45.0", 1290 | ] 1291 | 1292 | [[package]] 1293 | name = "tokio-rustls" 1294 | version = "0.23.4" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 1297 | dependencies = [ 1298 | "rustls", 1299 | "tokio", 1300 | "webpki", 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "tokio-util" 1305 | version = "0.7.7" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" 1308 | dependencies = [ 1309 | "bytes", 1310 | "futures-core", 1311 | "futures-sink", 1312 | "pin-project-lite", 1313 | "tokio", 1314 | "tracing", 1315 | ] 1316 | 1317 | [[package]] 1318 | name = "tower-service" 1319 | version = "0.3.2" 1320 | source = "registry+https://github.com/rust-lang/crates.io-index" 1321 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1322 | 1323 | [[package]] 1324 | name = "tracing" 1325 | version = "0.1.37" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 1328 | dependencies = [ 1329 | "cfg-if", 1330 | "pin-project-lite", 1331 | "tracing-core", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "tracing-core" 1336 | version = "0.1.30" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 1339 | dependencies = [ 1340 | "once_cell", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "try-lock" 1345 | version = "0.2.4" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" 1348 | 1349 | [[package]] 1350 | name = "unicode-bidi" 1351 | version = "0.3.13" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" 1354 | 1355 | [[package]] 1356 | name = "unicode-ident" 1357 | version = "1.0.8" 1358 | source = "registry+https://github.com/rust-lang/crates.io-index" 1359 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 1360 | 1361 | [[package]] 1362 | name = "unicode-normalization" 1363 | version = "0.1.22" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1366 | dependencies = [ 1367 | "tinyvec", 1368 | ] 1369 | 1370 | [[package]] 1371 | name = "unicode-segmentation" 1372 | version = "1.10.1" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 1375 | 1376 | [[package]] 1377 | name = "unicode-width" 1378 | version = "0.1.10" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 1381 | 1382 | [[package]] 1383 | name = "unsafe-libyaml" 1384 | version = "0.2.7" 1385 | source = "registry+https://github.com/rust-lang/crates.io-index" 1386 | checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" 1387 | 1388 | [[package]] 1389 | name = "untrusted" 1390 | version = "0.7.1" 1391 | source = "registry+https://github.com/rust-lang/crates.io-index" 1392 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1393 | 1394 | [[package]] 1395 | name = "url" 1396 | version = "2.3.1" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1399 | dependencies = [ 1400 | "form_urlencoded", 1401 | "idna", 1402 | "percent-encoding", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "utf8parse" 1407 | version = "0.2.1" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1410 | 1411 | [[package]] 1412 | name = "want" 1413 | version = "0.3.0" 1414 | source = "registry+https://github.com/rust-lang/crates.io-index" 1415 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1416 | dependencies = [ 1417 | "log", 1418 | "try-lock", 1419 | ] 1420 | 1421 | [[package]] 1422 | name = "wasi" 1423 | version = "0.10.0+wasi-snapshot-preview1" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1426 | 1427 | [[package]] 1428 | name = "wasi" 1429 | version = "0.11.0+wasi-snapshot-preview1" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1432 | 1433 | [[package]] 1434 | name = "wasm-bindgen" 1435 | version = "0.2.84" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 1438 | dependencies = [ 1439 | "cfg-if", 1440 | "wasm-bindgen-macro", 1441 | ] 1442 | 1443 | [[package]] 1444 | name = "wasm-bindgen-backend" 1445 | version = "0.2.84" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 1448 | dependencies = [ 1449 | "bumpalo", 1450 | "log", 1451 | "once_cell", 1452 | "proc-macro2", 1453 | "quote", 1454 | "syn 1.0.109", 1455 | "wasm-bindgen-shared", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "wasm-bindgen-futures" 1460 | version = "0.4.34" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" 1463 | dependencies = [ 1464 | "cfg-if", 1465 | "js-sys", 1466 | "wasm-bindgen", 1467 | "web-sys", 1468 | ] 1469 | 1470 | [[package]] 1471 | name = "wasm-bindgen-macro" 1472 | version = "0.2.84" 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" 1474 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 1475 | dependencies = [ 1476 | "quote", 1477 | "wasm-bindgen-macro-support", 1478 | ] 1479 | 1480 | [[package]] 1481 | name = "wasm-bindgen-macro-support" 1482 | version = "0.2.84" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 1485 | dependencies = [ 1486 | "proc-macro2", 1487 | "quote", 1488 | "syn 1.0.109", 1489 | "wasm-bindgen-backend", 1490 | "wasm-bindgen-shared", 1491 | ] 1492 | 1493 | [[package]] 1494 | name = "wasm-bindgen-shared" 1495 | version = "0.2.84" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 1498 | 1499 | [[package]] 1500 | name = "web-sys" 1501 | version = "0.3.61" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" 1504 | dependencies = [ 1505 | "js-sys", 1506 | "wasm-bindgen", 1507 | ] 1508 | 1509 | [[package]] 1510 | name = "webpki" 1511 | version = "0.22.0" 1512 | source = "registry+https://github.com/rust-lang/crates.io-index" 1513 | checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" 1514 | dependencies = [ 1515 | "ring", 1516 | "untrusted", 1517 | ] 1518 | 1519 | [[package]] 1520 | name = "winapi" 1521 | version = "0.3.9" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1524 | dependencies = [ 1525 | "winapi-i686-pc-windows-gnu", 1526 | "winapi-x86_64-pc-windows-gnu", 1527 | ] 1528 | 1529 | [[package]] 1530 | name = "winapi-i686-pc-windows-gnu" 1531 | version = "0.4.0" 1532 | source = "registry+https://github.com/rust-lang/crates.io-index" 1533 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1534 | 1535 | [[package]] 1536 | name = "winapi-util" 1537 | version = "0.1.5" 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" 1539 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1540 | dependencies = [ 1541 | "winapi", 1542 | ] 1543 | 1544 | [[package]] 1545 | name = "winapi-x86_64-pc-windows-gnu" 1546 | version = "0.4.0" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1549 | 1550 | [[package]] 1551 | name = "windows" 1552 | version = "0.48.0" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" 1555 | dependencies = [ 1556 | "windows-targets 0.48.0", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "windows-sys" 1561 | version = "0.42.0" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1564 | dependencies = [ 1565 | "windows_aarch64_gnullvm 0.42.2", 1566 | "windows_aarch64_msvc 0.42.2", 1567 | "windows_i686_gnu 0.42.2", 1568 | "windows_i686_msvc 0.42.2", 1569 | "windows_x86_64_gnu 0.42.2", 1570 | "windows_x86_64_gnullvm 0.42.2", 1571 | "windows_x86_64_msvc 0.42.2", 1572 | ] 1573 | 1574 | [[package]] 1575 | name = "windows-sys" 1576 | version = "0.45.0" 1577 | source = "registry+https://github.com/rust-lang/crates.io-index" 1578 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1579 | dependencies = [ 1580 | "windows-targets 0.42.2", 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "windows-targets" 1585 | version = "0.42.2" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 1588 | dependencies = [ 1589 | "windows_aarch64_gnullvm 0.42.2", 1590 | "windows_aarch64_msvc 0.42.2", 1591 | "windows_i686_gnu 0.42.2", 1592 | "windows_i686_msvc 0.42.2", 1593 | "windows_x86_64_gnu 0.42.2", 1594 | "windows_x86_64_gnullvm 0.42.2", 1595 | "windows_x86_64_msvc 0.42.2", 1596 | ] 1597 | 1598 | [[package]] 1599 | name = "windows-targets" 1600 | version = "0.48.0" 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" 1602 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 1603 | dependencies = [ 1604 | "windows_aarch64_gnullvm 0.48.0", 1605 | "windows_aarch64_msvc 0.48.0", 1606 | "windows_i686_gnu 0.48.0", 1607 | "windows_i686_msvc 0.48.0", 1608 | "windows_x86_64_gnu 0.48.0", 1609 | "windows_x86_64_gnullvm 0.48.0", 1610 | "windows_x86_64_msvc 0.48.0", 1611 | ] 1612 | 1613 | [[package]] 1614 | name = "windows_aarch64_gnullvm" 1615 | version = "0.42.2" 1616 | source = "registry+https://github.com/rust-lang/crates.io-index" 1617 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 1618 | 1619 | [[package]] 1620 | name = "windows_aarch64_gnullvm" 1621 | version = "0.48.0" 1622 | source = "registry+https://github.com/rust-lang/crates.io-index" 1623 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 1624 | 1625 | [[package]] 1626 | name = "windows_aarch64_msvc" 1627 | version = "0.42.2" 1628 | source = "registry+https://github.com/rust-lang/crates.io-index" 1629 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 1630 | 1631 | [[package]] 1632 | name = "windows_aarch64_msvc" 1633 | version = "0.48.0" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 1636 | 1637 | [[package]] 1638 | name = "windows_i686_gnu" 1639 | version = "0.42.2" 1640 | source = "registry+https://github.com/rust-lang/crates.io-index" 1641 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 1642 | 1643 | [[package]] 1644 | name = "windows_i686_gnu" 1645 | version = "0.48.0" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 1648 | 1649 | [[package]] 1650 | name = "windows_i686_msvc" 1651 | version = "0.42.2" 1652 | source = "registry+https://github.com/rust-lang/crates.io-index" 1653 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 1654 | 1655 | [[package]] 1656 | name = "windows_i686_msvc" 1657 | version = "0.48.0" 1658 | source = "registry+https://github.com/rust-lang/crates.io-index" 1659 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 1660 | 1661 | [[package]] 1662 | name = "windows_x86_64_gnu" 1663 | version = "0.42.2" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 1666 | 1667 | [[package]] 1668 | name = "windows_x86_64_gnu" 1669 | version = "0.48.0" 1670 | source = "registry+https://github.com/rust-lang/crates.io-index" 1671 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 1672 | 1673 | [[package]] 1674 | name = "windows_x86_64_gnullvm" 1675 | version = "0.42.2" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 1678 | 1679 | [[package]] 1680 | name = "windows_x86_64_gnullvm" 1681 | version = "0.48.0" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 1684 | 1685 | [[package]] 1686 | name = "windows_x86_64_msvc" 1687 | version = "0.42.2" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 1690 | 1691 | [[package]] 1692 | name = "windows_x86_64_msvc" 1693 | version = "0.48.0" 1694 | source = "registry+https://github.com/rust-lang/crates.io-index" 1695 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 1696 | 1697 | [[package]] 1698 | name = "winreg" 1699 | version = "0.10.1" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 1702 | dependencies = [ 1703 | "winapi", 1704 | ] 1705 | --------------------------------------------------------------------------------