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
use std::{io, str::FromStr};

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

#[allow(clippy::enum_variant_names)]
#[derive(Eq, PartialEq, Debug, Clone, clap::ArgEnum, Copy)]
pub enum Shell {
    Bash,
    Elvish,
    Fish,
    PowerShell,
    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()
    }
}

#[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,
}

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),
    }
}