1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Tooling for generating completions

use std::{io, str::FromStr};

use clap::App;
use clap_generate::{generate, generators};
use miette::{Diagnostic, SourceSpan};
use quickcheck::{Arbitrary, Gen};
use thiserror::Error;

/// The shell the user has selected
#[allow(clippy::enum_variant_names)]
#[derive(Eq, PartialEq, Debug, Clone, clap::ArgEnum, Copy)]
pub enum Shell {
    /// Generate for bash
    Bash,
    /// Generate for elvish
    Elvish,
    /// Generate for Fish
    Fish,
    /// Generate for powershell
    PowerShell,
    /// Generate for zsh
    Zsh,
}

impl Arbitrary for Shell {
    fn arbitrary(g: &mut Gen) -> Self {
        *g.choose(&[
            Self::Bash,
            Self::Elvish,
            Self::Fish,
            Self::PowerShell,
            Self::Zsh,
        ])
        .unwrap()
    }

    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
        let options = [
            Self::Bash,
            Self::Elvish,
            Self::Fish,
            Self::PowerShell,
            Self::Zsh,
        ];
        let index = options.iter().position(|other| self.eq(other));

        match index {
            None | Some(0) => quickcheck::empty_shrinker(),
            Some(index) => options
                .get(index - 1)
                .map_or(quickcheck::empty_shrinker(), |item| {
                    quickcheck::single_shrinker(*item)
                }),
        }
    }
}

impl FromStr for Shell {
    type Err = ShellFromStrError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "bash" => Ok(Self::Bash),
            "fish" => Ok(Self::Fish),
            "elvish" => Ok(Self::Elvish),
            "powershell" => Ok(Self::PowerShell),
            "zsh" => Ok(Self::Zsh),
            _ => Err(ShellFromStrError {
                source_code: s.to_string(),
                underline: (0, s.len()).into(),
            }),
        }
    }
}

impl From<Shell> for String {
    fn from(shell: Shell) -> Self {
        match shell {
            Shell::Bash => "bash",
            Shell::Elvish => "elvish",
            Shell::Fish => "fish",
            Shell::PowerShell => "powershell",
            Shell::Zsh => "zsh",
        }
        .to_string()
    }
}

/// Error when we could not parse a shell from the given string
#[derive(Debug, Eq, PartialEq, Error, Diagnostic)]
#[error("could not parse a shell from the given string")]
#[diagnostic(
    url(docsrs),
    help("valid shells are: bash, elvish, fish, powershell, and zsh")
)]
pub struct ShellFromStrError {
    #[source_code]
    source_code: String,
    #[label("unknown shell")]
    underline: SourceSpan,
}

/// Print completion for the given shell
pub fn print_completions(writer: &mut dyn io::Write, app: &mut App<'_>, shell: Shell) {
    match shell {
        Shell::Bash => generate::<_, _>(generators::Bash, app, app.get_name().to_string(), writer),
        Shell::Elvish => {
            generate::<_, _>(generators::Elvish, app, app.get_name().to_string(), writer);
        }
        Shell::Fish => generate::<_, _>(generators::Fish, app, app.get_name().to_string(), writer),
        Shell::PowerShell => {
            generate::<_, _>(
                generators::PowerShell,
                app,
                app.get_name().to_string(),
                writer,
            );
        }
        Shell::Zsh => generate::<_, _>(generators::Zsh, app, app.get_name().to_string(), writer),
    }
}