Skip to content

Commit

Permalink
fix: Make order of options deterministic
Browse files Browse the repository at this point in the history
  • Loading branch information
ysthakur committed Aug 8, 2023
1 parent ba80c2d commit b3a76d4
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
package-name: man-completions
release-type: rust
- uses: actions/checkout@v3
if: ${{ steps.release.outputs.release_created }}
# if: ${{ steps.release.outputs.release_created }}
# Publish to Cargo if release created
- run: cargo publish --verbose --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
# if: ${{ steps.release.outputs.release_created }}
Expand Down
103 changes: 103 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ clap = { version = "4.3", features = ["derive"] }
flate2 = "1.0"
regex = "1.9"

[dev-dependencies]
assert_cmd = "2.0"

[[bin]]
name = "man-completions"
path = "src/main.rs"
17 changes: 17 additions & 0 deletions src/gen/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::{fs, path::Path};

use crate::parse::CommandInfo;

use super::Completions;
use anyhow::Result;

pub struct JsonCompletions;

impl Completions for JsonCompletions {
fn generate<P>(cmd_name: String, cmd_info: CommandInfo, out_dir: P) -> Result<()>
where
P: AsRef<Path>,
{
Ok(())
}
}
9 changes: 7 additions & 2 deletions src/gen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
mod json;
mod zsh;

use std::path::Path;

use crate::parse::CommandInfo;
use anyhow::Result;

pub mod zsh;
pub use json::*;
pub use zsh::*;

pub trait Completions {
fn generate<P>(cmd_name: String, cmd_info: CommandInfo, out_dir: P) -> Result<()>
Expand All @@ -12,10 +16,11 @@ pub trait Completions {

fn generate_all<I, P>(cmds: I, out_dir: P) -> Result<()>
where
I: Iterator<Item = (String, CommandInfo)>,
I: IntoIterator<Item = (String, CommandInfo)>,
P: AsRef<Path>,
{
cmds
.into_iter()
.map(|(cmd_name, cmd_info)| <Self as Completions>::generate(cmd_name, cmd_info, &out_dir))
.collect()
}
Expand Down
4 changes: 2 additions & 2 deletions src/gen/zsh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{fs, path::Path};
use crate::parse::CommandInfo;

use super::Completions;
use crate::Result;
use anyhow::Result;

/// Indentation to use (for readability)
const INDENT: &str = " ";
Expand Down Expand Up @@ -81,7 +81,7 @@ fn generate_fn(
if !cmd_info.subcommands.is_empty() {
out.push_str(&format!("{}{}", INDENT, "local line\n"));
}
out.push_str(&format!("{INDENT} _arguments -C \\\n"));
out.push_str(&format!("{INDENT}_arguments -C \\\n"));
for opt in cmd_info.args {
for form in opt.forms {
let text = quote(&format!("{form}[{}]", opt.desc));
Expand Down
8 changes: 5 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod parse;
use std::{collections::HashMap, path::PathBuf};

use crate::{
gen::{zsh::ZshCompletions, Completions},
gen::{Completions, JsonCompletions, ZshCompletions},
parse::{parse_manpage_at_path, parse_manpage_text, read_manpage, CommandInfo},
};
use anyhow::{anyhow, Result};
Expand All @@ -16,6 +16,8 @@ use std::process::Command;
#[derive(Debug, Clone, ValueEnum)]
enum Shell {
Zsh,
/// Not a shell, but output the parsed options as JSON
Json,
}

/// Generate completions from manpages
Expand Down Expand Up @@ -61,9 +63,9 @@ fn section_num_parser(s: &str) -> core::result::Result<u8, String> {
}

fn gen_shell(shell: Shell, manpages: HashMap<String, CommandInfo>, out_dir: &Path) -> Result<()> {
// println!("{:?}", &manpages);
match shell {
Shell::Zsh => <ZshCompletions as Completions>::generate_all(manpages.into_iter(), out_dir),
Shell::Zsh => <ZshCompletions as Completions>::generate_all(manpages, out_dir),
Shell::Json => <JsonCompletions as Completions>::generate_all(manpages, out_dir),
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct CommandInfo {

#[derive(Debug)]
pub struct Arg {
pub forms: HashSet<String>,
pub forms: Vec<String>,
pub desc: String,
}

Expand All @@ -39,7 +39,8 @@ where
decoder.read_to_string(&mut str)?;
Ok(str)
} else {
todo!()
let contents = std::fs::read_to_string(path)?;
Ok(contents)
}
}
None => todo!(),
Expand Down
6 changes: 3 additions & 3 deletions src/parse/parse_man.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;

use regex::{Regex, RegexBuilder};

Expand Down Expand Up @@ -54,7 +54,7 @@ pub fn parse(_cmd_name: &str, page_text: &str) -> Result<Option<CommandInfo>> {

// Copied more or less directly from Fish's `built_command`
fn make_arg(options: &str, desc: &str) -> Arg {
let mut forms = HashSet::new();
let mut forms = Vec::new();

// Unquote the options
let options = if options.len() == 1 {
Expand All @@ -78,7 +78,7 @@ fn make_arg(options: &str, desc: &str) -> Arg {
if Regex::new(r"\{\}\(\)").unwrap().is_match(option) {
continue;
}
forms.insert(option.to_owned());
forms.push(option.to_owned());
}

let desc = desc.trim().replace("\n", " ");
Expand Down
22 changes: 19 additions & 3 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
// use assert_cmd::prelude::*;
use std::{env, fs, path::PathBuf, process::Command};

use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt};

const BIN_NAME: &str = "man-completions";

/// Shells to test
const SHELLS: [&str; 2] = ["zsh", "json"];

#[test]
fn test() {
// The project's root directory
Expand All @@ -9,11 +17,19 @@ fn test() {
let in_dir = test_resources.join("in");
let expected_dir = test_resources.join("expected");
let out_dir = test_resources.join("tmp");
if !out_dir.exists() {
fs::create_dir(&out_dir);
}

// The man-completions binary to test
let bin = env::var("CARGO_BIN_EXE_man-completions").unwrap();
let status = Command::new(bin).env("MANPATH", in_dir).status().unwrap();
assert!(status.success());
for shell in SHELLS {
let mut cmd = Command::cargo_bin(BIN_NAME).unwrap();
let cmd =
cmd
.env("MANPATH", &in_dir)
.args(["--shell", shell, "--out", &out_dir.display().to_string()]);
cmd.assert().success();
}

// Files that didn't get generated
let mut not_generated = Vec::new();
Expand Down
9 changes: 5 additions & 4 deletions tests/resources/expected/_git.zsh
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
#compdef _git git

function _git {
_arguments -C \
_arguments -C \
'-v[Prints the Git suite version that the git program came from. .sp This option ...]' \
'--version[Prints the Git suite version that the git program came from. .sp This option ...]' \
'--help[Prints the synopsis and a list of the most commonly used commands. If the opt...]' \
'-h[Prints the synopsis and a list of the most commonly used commands. If the opt...]' \
'--help[Prints the synopsis and a list of the most commonly used commands. If the opt...]' \
'-C[Run as if git was started in <path> instead of the current working directory....]' \
'-c[Pass a configuration parameter to the command. The value given will override ...]' \
'--config-env[Like -c <name>=<value>, give configuration variable <name> a value, where <en...]' \
'--exec-path[Path to wherever your core Git programs are installed. This can also be contr...]' \
'--html-path[Print the path, without trailing slash, where Git'"'"'s HTML documentation is ins...]' \
'--man-path[Print the manpath (see man(1)) for the man pages for this version of Git and ...]' \
'--info-path[Print the path where the Info files documenting this version of Git are insta...]' \
'--paginate[Pipe all output into less (or if set, $PAGER) if standard output is a termina...]' \
'-p[Pipe all output into less (or if set, $PAGER) if standard output is a termina...]' \
'--no-pager[Do not pipe Git output into a pager]' \
'--paginate[Pipe all output into less (or if set, $PAGER) if standard output is a termina...]' \
'-P[Do not pipe Git output into a pager]' \
'--no-pager[Do not pipe Git output into a pager]' \
'--git-dir[Set the path to the repository (".git" directory). This can also be controlle...]' \
'--work-tree[Set the path to the working tree. It can be an absolute path or a path relati...]' \
'--namespace[Set the Git namespace. See gitnamespaces(7) for more details. Equivalent to s...]' \
Expand Down

0 comments on commit b3a76d4

Please sign in to comment.