gpt-cli 0.1.3

Run linux commands with natural language. Eg.: 'show my graphic card' instead 'lspci | grep VGA'
extern crate core;

mod cache;
mod decompose;
mod get_postprocess_action;
mod gpt3;
mod should_exit;

use inquire::Confirm;

use colored::*;
use std::env;

use crate::get_postprocess_action::{get_postprocess_action, PostprocessAction};
use crate::gpt3::Gpt3Message;
use crate::should_exit::{should_exit, ShouldExit};
use std::process::{Command, Stdio};
use tokio::runtime::Runtime;

fn postprocess(answer_text: &String) {
    let action = get_postprocess_action(answer_text);

    match action {
        PostprocessAction::Confirm => {
            let ans = Confirm::new(&format!("Execute.:\n\n{}\n\n", answer_text.green()))
                .with_default(true)
                .with_help_message("Pressing enter you confirm execution of this command")
                .prompt();

            match ans {
                Ok(true) => {
                    let (command_name, command_args) = decompose::decompose(answer_text);

                    let mut child = Command::new(command_name)
                        .args(command_args)
                        .stdin(Stdio::inherit())
                        .stdout(Stdio::inherit())
                        .stderr(Stdio::inherit())
                        .spawn()
                        .expect("Failed to execute command");

                    child.wait().expect("Failed to wait for command");
                }
                Ok(false) => println!("That's too bad, I've heard great things about it."),
                Err(_) => println!("Error with questionnaire, try again later"),
            }
        }
        PostprocessAction::Copy => {
            #[cfg(not(target_env = "musl"))]
            {
                terminal_clipboard::set_string(answer_text).unwrap();
                assert_eq!(*answer_text, terminal_clipboard::get_string().unwrap());
                println!("Text '{answer_text}' was copied to your clipboard")
            }
            #[cfg(target_env = "musl")]
            {
                println!("{}", answer_text);
            }
        }
        PostprocessAction::Out => {
            println!("{}", answer_text);
        }
    }
}

fn exit_with_messages_if_required(should_exit: ShouldExit) {
    let ShouldExit {
        exit,
        messages,
        is_error,
    } = should_exit;

    if exit {
        for message in messages.iter() {
            if is_error {
                eprintln!("{}", message);
            } else {
                println!("{}", message)
            }
        }
        std::process::exit(if is_error { 1 } else { 0 });
    }
}

async fn async_main() {
    let args: Vec<String> = env::args().skip(1).collect();
    exit_with_messages_if_required(should_exit(&args));

    let content = args.join(" ");
    let rt = Runtime::new().unwrap();

    rt.block_on(async {
        let client = gpt3::Gpt::new(Some(false), None);
        let response = client
            .ask(vec![
                Gpt3Message {
                    content: gpt3::Gpt::get_system_prompt(),
                    role: String::from("system"),
                },
                Gpt3Message {
                    role: String::from("user"),
                    content,
                },
            ])
            .await;

        match response {
            Err(error) => {
                let mut messages = vec![error.red()];
                if error == *"Error: OPENAI_API_KEY environment variable is not defined." {
                    messages.push(
                        "Please set the OPENAI_API_KEY environment variable to your OpenAI API key."
                            .normal(),
                    );
                }
                exit_with_messages_if_required(ShouldExit {
                    is_error: true,
                    exit: true,
                    messages,
                });
            }
            Ok(data) => {
                let choice = data.choices.first().expect("No choice in response");
                let answer_text = &choice.message.content;

                postprocess(answer_text);
            }
        }
    });
}

fn main() {
    futures::executor::block_on(async_main());
}