From 6b4511b1ef811c8511ec78c809d2952a2e7be7b3 Mon Sep 17 00:00:00 2001 From: Adrian Covaci <6562353+acovaci@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:51:42 +0100 Subject: [PATCH] chore: formatting --- .github/FUNDING.yml | 14 +-- .github/dependabot.yml | 12 +-- .pre-commit-config.yaml | 46 +++++++++ .yamllint | 3 + Cargo.lock | 14 +++ Cargo.toml | 2 + README.md | 1 - src/cli/init.rs | 8 +- src/command/chat.rs | 2 +- src/core/context.rs | 222 +++++++++++++++++++++------------------- src/core/example.rs | 2 +- 11 files changed, 190 insertions(+), 136 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 .yamllint diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 09b12cc..88ee3c5 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,15 +1,5 @@ +--- # These are supported funding model platforms -github: +github: - acovaci -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -polar: # Replace with a single Polar username -buy_me_a_coffee: # Replace with a single Buy Me a Coffee username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5251d0f..f9e376a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,7 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - +--- version: 2 updates: - - package-ecosystem: "cargo" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: cargo + directory: / schedule: - interval: "weekly" + interval: weekly diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a71065f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,46 @@ +--- +minimum_pre_commit_version: 3.7.0 +default_install_hook_types: [pre-commit, commit-msg] +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-yaml + types: [yaml] + - id: check-toml + types: [toml] + - id: check-executables-have-shebangs + types_or: [shell, text] + - id: check-shebang-scripts-are-executable + types_or: [shell, text] + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.5 + hooks: + - id: remove-crlf + - repo: https://github.com/doublify/pre-commit-rust + rev: v1.0 + hooks: + - id: fmt + types: [rust] + - id: cargo-check + types: [rust] + - id: clippy + types: [rust] + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.35.1 + hooks: + - id: yamllint + args: [--format, parsable, --strict] + - repo: https://github.com/executablebooks/mdformat + rev: 0.7.17 + hooks: + - id: mdformat + args: [--wrap=100, --number] + types: [markdown] + - repo: https://github.com/compilerla/conventional-pre-commit + rev: v3.2.0 + hooks: + - id: conventional-pre-commit + stages: [commit-msg] diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..fea00cc --- /dev/null +++ b/.yamllint @@ -0,0 +1,3 @@ +rules: + indentation: + spaces: consistent diff --git a/Cargo.lock b/Cargo.lock index f951ba1..4d2aace 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -759,6 +759,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "multiline-str" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c122ad41147754c50db0d7420f743efcc974635fa767486cc9fb9382b6cbd1a" + [[package]] name = "native-tls" version = "0.2.12" @@ -1217,9 +1223,11 @@ dependencies = [ "log", "log4rs", "minijinja", + "multiline-str", "openai_dive", "reqwest", "serde", + "text-block-macros", "thiserror", "tokio", ] @@ -1335,6 +1343,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "text-block-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8b59b4da1c1717deaf1de80f0179a9d8b4ac91c986d5fd9f4a8ff177b84049" + [[package]] name = "thiserror" version = "1.0.61" diff --git a/Cargo.toml b/Cargo.toml index 48d1763..1806909 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,11 @@ home = "0.5.9" log = "0.4.21" log4rs = "1.3.0" minijinja = "2.0.1" +multiline-str = "0.1.1" openai_dive = "0.4.8" reqwest = { version = "0.12.4", features = ["json"] } serde = { version = "1.0.203", features = ["derive"] } +text-block-macros = "0.1.1" thiserror = "1.0.61" tokio = { version = "1.38.0", features = ["rt", "rt-multi-thread", "macros", "io-std"] } diff --git a/README.md b/README.md index 8f27ca7..5039a01 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,3 @@ I would especially welcome contributions in the following areas: This project is licensed under the GNU Affero General Public License v3.0. You can find the full license text in the [LICENSE](LICENSE) file. - diff --git a/src/cli/init.rs b/src/cli/init.rs index 4df9751..e0e21ee 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -105,13 +105,7 @@ pub fn get_config_text() -> Res>> { for (alias, command) in ALIASES.iter() { buf.write_all( - format!( - "alias '{}'='{} {}'", - alias, - crate_dir.display().to_string(), - command - ) - .as_bytes(), + format!("alias '{}'='{} {}'", alias, crate_dir.display(), command).as_bytes(), )?; buf.write_all(&[b'\n'])?; } diff --git a/src/command/chat.rs b/src/command/chat.rs index 5a452ef..135c9e9 100644 --- a/src/command/chat.rs +++ b/src/command/chat.rs @@ -27,6 +27,6 @@ impl ChatContext { content: response.clone(), }); log::debug!("Chat history: {:?}", self.history); - return Ok(response); + Ok(response) } } diff --git a/src/core/context.rs b/src/core/context.rs index 979341b..a311f61 100644 --- a/src/core/context.rs +++ b/src/core/context.rs @@ -1,3 +1,5 @@ +use multiline_str::multiline_str; + pub enum ModelContext { ShellCompletion, ShellExplanation, @@ -9,19 +11,21 @@ impl ModelContext { pub fn to_string(&self) -> Option { match self { ModelContext::ShellCompletion => { - let instructions = vec![ - "You are part of a command-line interface that provides auto-completion for shell", - "commands. You will get part of a command and you need to provide the full command,", - "including the input given by the user.", - "Because of this, any reply that contains text that is not a valid command will be", - "rejected. If you are unable to provide a valid command, you can include [NULL] in your", - "response to indicate that you do not have a valid completion. To be clear, any response", - "containing [NULL] will be treated as a null response, regardless of any other text in", - "the response. Any response that is not a valid command and does not contain [NULL] will", - "be rejected. You might receive requests containing popular command line programs, you", - "must provide completion for those as well, even though they're not built-in commands.", - ] - .join(" "); + let instructions = multiline_str! { + "You are part of a command-line interface that provides auto-completion for,", + "shell commands. You will get part of a command and you need to provide the", + "full command, including the input given by the user."; + "Because of this, any reply that contains text that is not a valid command", + "will be rejected. If you are unable to provide a valid command, you can", + "include [NULL] in your response to indicate that you do not have a valid", + "completion. To be clear, any response containing [NULL] will be treated as a", + "null response, regardless of any other text in the response. Any response", + "that is not a valid command and does not contain [NULL] will be rejected."; + "You might receive requests containing popular command line programs, you", + "must provide completion for those as well, even though they're not built-in", + "commands." + } + .to_string(); let examples = vec![ super::example::Example { input: "ls -l /home/user/.c".to_string(), @@ -41,123 +45,129 @@ impl ModelContext { }, ] .into_iter() - .map(|example| example.to_string()) + .map(|example| example.into_string()) .collect::>() .join("\n"); Some(format!("{}\n\nExamples:{}", instructions, examples)) } ModelContext::ShellExplanation => { - let instructions = vec![ - "You are part of a command-line interface that provides explanations for shell", - "commands. You will get a full command and you need to provide an explanation for", - "it. The explanation should be a short description of what the command does, as", - "well as common use cases for the command. Include practical examples of how the", - "command is used, and any other relevant information that would help a user", - ] - .join(" "); + let instructions = multiline_str! { + "You are part of a command-line interface that provides explanations for shell", + "commands. You will get a full command and you need to provide an explanation", + "for it. The explanation should be a short description of what the command", + "does, as well as common use cases for the command. Include practical examples", + "of how the command is used, and any other relevant information that would", + "help a user." + }; let examples = vec![ super::example::Example { input: "ls -l /home/user/.config".to_string(), - output: vec![ + output: multiline_str! { "The `ls` command is commonly used to list files and directories in a", "directory. The `-l` flag is used to display the files and directories", "in a long format, which includes additional information such as file", - "permissions, owner, group, size, and modification time.\n", - "\n", + "permissions, owner, group, size, and modification time."; + ""; "The `/home/user/.config` argument specifies the directory to list. In", "this case, the command will list the files and directories in the", "`/home/user/.config` directory. This directory is commonly used to", - "store configuration files for various applications.\n", - "\n", - "Command usage:\n", - "ls [OPTION] [FILE]\n", - "\n", - "Common options:\n", - "`-l`: Display files and directories in long format (one per line)\n", - "`-a`: Include hidden files and directories\n", - "`-h`: Display file sizes in human-readable format\n", - "`-t`: Sort files and directories by modification time\n", - "`-r`: Reverse the order of the sort\n", - "\n", - "Examples:\n", - "`ls -l /home/user/.config`: List files and directories in the\n", - "\t`/home/user/.config` directory in long format\n", - "`ls -a /usr/bin`: List all files and directories in the `/usr/bin`\n", - "\tdirectory, including hidden files\n", - "`ls -rlh /var/log`: List files and directories in the `/var/log`\n", - "\tdirectory in reverse order\n", - ] - .join(""), + "store configuration files for various applications."; + ""; + "Command usage:"; + "ls [OPTION] [FILE]"; + ""; + "Common options:"; + "`-l`: Display files and directories in long format (one per line)"; + "`-a`: Include hidden files and directories"; + "`-h`: Display file sizes in human-readable format"; + "`-t`: Sort files and directories by modification time"; + "`-r`: Reverse the order of the sort"; + ""; + "Examples:"; + "`ls -l /home/user/.config`:"; + "\tList files and directories in the `/home/user/.config` directory in", + "long format"; + "`ls -a /usr/bin`:"; + "\tList all files and directories in the `/usr/bin` directory,", + "including hidden files"; + "`ls -rlh /var/log`:"; + "\tList files and directories in the `/var/log` directory in reverse", + "order" + } + .to_string(), }, super::example::Example { input: "dd of=\"usb_dump.bin\" if=\"/dev/sda\"".to_string(), - output: vec![ + output: multiline_str! { "The `dd` command is commonly used to convert and copy files. The `of`", "argument specifies the output file, while the `if` argument specifies", "the input file. In this case, the command will copy the contents of", "the device `/dev/sda` (commonly a storage device, for example a USB", - "drive) to the binary file `usb_dump.bin`.\n", - "\n", - "Command usage:\n", - "dd [OPTION] if=[FILE] of=[FILE]\n", - "\n", - "Common options:\n", - "`if=`: Specify the input file\n", - "`of=`: Specify the output file\n", + "drive) to the binary file `usb_dump.bin`."; + ""; + "Command usage:"; + "dd [OPTION] if=[FILE] of=[FILE]"; + ""; + "Common options:"; + "`if=`: Specify the input file"; + "`of=`: Specify the output file"; "`bs=`: Specify the block size for copying (how many bytes to", - "copy at a time). By default, `dd` uses a block size of 512 bytes.\n", + "copy at a time). By default, `dd` uses a block size of 512 bytes."; "`count=`: Specify the number of blocks to copy. By default, `dd`", - "copies until the end of the input file.\n", - "`status=progress`: Display the progress of the copy operation.\n", - "`skip=`: Skip a specified number of bytes before copying.\n", - "\n", - "Examples:\n", - "`dd if=/dev/sda of=usb_dump.bin`: Copy the contents of the device", - "`/dev/sda` to the file `usb_dump.bin`\n", - "`dd if=/dev/zero of=zeroes.bin bs=1M count=1`: Create a file", - "`zeroes.bin` filled with zeroes, with a size of 1 megabyte\n", - "`dd if=/dev/random of=random.bin bs=1M count=1`: Create a file", - "`random.bin` filled with random data, with a size of 1 megabyte\n", - ] - .join(""), + "copies until the end of the input file."; + "`status=progress`: Display the progress of the copy operation."; + "`skip=`: Skip a specified number of bytes before copying."; + ""; + "Examples:"; + "`dd if=/dev/sda of=usb_dump.bin`:"; + "\tCopy the contents of the device `/dev/sda` to the file", + "`usb_dump.bin`"; + "`dd if=/dev/zero of=zeroes.bin bs=1M count=1`:"; + "\tCreate a file `zeroes.bin` filled with zeroes, with a size of 1", + "megabyte"; + "`dd if=/dev/random of=random.bin bs=1M count=1`:"; + "\tCreate a file `random.bin` filled with random data, with a size of", + "1 megabyte" + } + .to_string(), }, super::example::Example { input: "echo \"Hello, world!\"".to_string(), - output: vec![ + output: multiline_str! { "The `echo` command is commonly used to display text on the terminal.", "In this case, the command will display the text `Hello, world!` on", - "the terminal.\n", - "\n", + "the terminal."; + ""; "By default, the `echo` command will display the text followed by a", "newline character. If you want to display the text without a newline", "character, you can use the `-n` option. Additionally, by default, the", "text will be displayed on the standard output (usually the terminal),", "howver, you can redirect the output to a file, another command, or", - "the standard error using redirection operators.\n", - "\n", - "Command usage:\n", - "echo [OPTION] [STRING]\n", - "\n", - "Common options:\n", - "`-n`: Do not output the trailing newline\n", - "\n", - "Examples:\n", - "`echo \"Hello, world!\"`: Display `Hello, world!` followed by a", - "\tnewline character\n", - ] - .join(""), + "the standard error using redirection operators."; + ""; + "Command usage:"; + "echo [OPTION] [STRING]"; + ""; + "Common options:"; + "`-n`: Do not output the trailing newline"; + ""; + "Examples:"; + "`echo \"Hello, world!\"`:"; + "\tDisplay `Hello, world!` followed by a newline character" + } + .to_string(), }, super::example::Example { input: "afhhi12412k".to_string(), - output: vec![ + output: multiline_str! { "The command `afhhi12412k` is not a valid command, and no close", - "matches were found. Please check the command and try again.", - ] - .join(""), + "matches were found. Please check the command and try again." + } + .to_string(), }, ] .into_iter() - .map(|example| example.to_string()) + .map(|example| example.into_string()) .collect::>() .join("\n"); Some(format!( @@ -166,21 +176,21 @@ impl ModelContext { )) } ModelContext::ShellGeneration => { - let instructions = vec![ - "You are part of a command-line interface that provides command generation for shell", - "commands. You will get a description of a command and you need to provide the full", - "command. The description will include the name of the command, as well as any", - "arguments or options that are required. You should generate a command that matches", - "the description as closely as possible.", - "If you are unable to generate a command based on the description, you can include", - "[NULL] in your response to indicate that you do not have a valid command. To be", - "clear, any response containing [NULL] will be treated as a null response, regardless", - "of any other text in the response. Any response that is not a valid command and does", - "not contain [NULL] will be rejected.", - "You might receive requests containing popular command line programs, you must provide", - "generation for those as well, even though they're not built-in commands.", - ] - .join(" "); + let instructions = multiline_str! { + "You are part of a command-line interface that provides command generation for", + "shell commands. You will get a description of a command and you need to", + "provide the full command. The description will include the name of the", + "command, as well as any arguments or options that are required. You should", + "generate a command that matches the description as closely as possible."; + "If you are unable to generate a command based on the description, you can"," + include [NULL] in your response to indicate that you do not have a valid", + "command. To be clear, any response containing [NULL] will be treated as a", + "null response, regardless of any other text in the response. Any response", + "that is not a valid command and does not contain [NULL] will be rejected."; + "You might receive requests containing popular command line programs, you must", + "provide generation for those as well, even though they're not built-in", + "commands." + }; let examples = vec![ super::example::Example { input: "List all files and directories in the /home/user/.config directory in long format".to_string(), @@ -202,7 +212,7 @@ impl ModelContext { input: "List all files and directories in the /home/user/.config directory in long format".to_string(), output: "ls -l /home/user/.config".to_string(), }, - ].into_iter().map(|example| example.to_string()).collect::>().join("\n"); + ].into_iter().map(|example| example.into_string()).collect::>().join("\n"); Some(format!("{}\n\nExamples:{}", instructions, examples)) } ModelContext::Chat => None, diff --git a/src/core/example.rs b/src/core/example.rs index 555206c..ce96bfd 100644 --- a/src/core/example.rs +++ b/src/core/example.rs @@ -4,7 +4,7 @@ pub struct Example { } impl Example { - pub fn to_string(&self) -> String { + pub fn into_string(&self) -> String { format!("Input: {} -> Output: {}", self.input, self.output) } }