Module: Clyops

Defined in:
lib/clyops.rb,
lib/clyops/completions.rb

Overview

Generated by tools/sync-completions.py from spec/completions/. Do not edit.

Defined Under Namespace

Classes: Cli, DefinitionError, ParseResult, ValidationError, Values

Constant Summary collapse

VERSION =

Release version of this library.

"0.3.0"
BASH_COMPLETION =
<<'CLYOPS_BASH_TEMPLATE'
# bash completion for __CLYOPS_PROG__ (generated by clyops)
# Enable with:  eval "$(__CLYOPS_PROG__ --completion bash)"
_clyops___CLYOPS_FUNC__() {
    local cur="${COMP_WORDS[COMP_CWORD]}" prev="" data rec name short type kind values rest i skip=0
    local -A otype=() okind=() ovalues=()
    local -a names=() cmds=() akind=() avalues=() aarity=()
    (( COMP_CWORD > 0 )) && prev="${COMP_WORDS[COMP_CWORD-1]}"
    # The words before the cursor let a dispatcher answer for the subcommand being typed.
    data=$("${COMP_WORDS[0]}" --bash-completion -- "${COMP_WORDS[@]:1:COMP_CWORD-1}" 2>/dev/null) || return 0
    [[ "$data" == "#clyops-completion 1"* ]] || return 0

    while IFS=$'\t' read -r rec name short type kind values rest; do
        [[ "$values" == - ]] && values=""
        case "$rec" in
            skip) skip="$name" ;;
            cmd) cmds+=("$name") ;;
            opt)
                for i in "$name" "$short"; do
                    [[ -z "$i" || "$i" == - ]] && continue
                    names+=("$i"); otype[$i]="$type"; okind[$i]="$kind"; ovalues[$i]="$values"
                done ;;
            arg)
                # arg records: name, arity, kind, values, description
                [[ "$kind" == - ]] && kind=""
                aarity+=("$short"); akind+=("$type"); avalues+=("$kind") ;;
        esac
    done <<< "$data"
    # Words up to and including a dispatched subcommand belong to the dispatcher.
    local first=$(( 1 + skip ))
    (( COMP_CWORD <= first )) && prev=""

    # Option value: --opt=VALUE (bash splits on "=") or --opt VALUE.
    local opt=""
    if [[ "$cur" == --*=* ]]; then opt="${cur%%=*}"; cur="${cur#*=}"
    elif [[ "$cur" == = && "${otype[$prev]}" == value ]]; then opt="$prev"; cur=""
    elif [[ "$prev" == = ]] && (( COMP_CWORD > first + 1 )); then opt="${COMP_WORDS[COMP_CWORD-2]}"
    elif [[ "$prev" == -* && "${otype[$prev]}" == value ]]; then opt="$prev"
    fi
    if [[ -n "$opt" ]]; then
        _clyops_value___CLYOPS_FUNC__ "${okind[$opt]}" "${ovalues[$opt]}" "$cur"
        return 0
    fi

    if [[ "$cur" == -* ]]; then
        COMPREPLY=($(compgen -W "${names[*]}" -- "$cur"))
        return 0
    fi

    # A dispatcher group: complete its subcommands.
    if (( ${#cmds[@]} )); then
        COMPREPLY=($(compgen -W "${cmds[*]}" -- "$cur"))
        return 0
    fi

    # A dispatched program without completion data: fall back to file names.
    if (( skip > 0 && ${#names[@]} == 0 && ${#akind[@]} == 0 )); then
        compopt -o default 2>/dev/null
        COMPREPLY=()
        return 0
    fi

    # Positional: count earlier positionals, skipping options and their values.
    local pos=0 endopts="" w
    for (( i = first; i < COMP_CWORD; i++ )); do
        w="${COMP_WORDS[i]}"
        if [[ -z "$endopts" && "$w" == -- ]]; then endopts=1
        elif [[ -z "$endopts" && "$w" == -?* ]]; then
            [[ "$w" != *=* && "${otype[$w]}" == value ]] && (( i++ ))
        elif [[ "$w" != = ]]; then (( pos++ ))
        fi
    done
    local n=${#akind[@]}
    (( n == 0 )) && return 0
    if (( pos >= n )); then
        [[ "${aarity[n-1]}" == variadic ]] || return 0
        pos=$(( n - 1 ))
    fi
    _clyops_value___CLYOPS_FUNC__ "${akind[pos]}" "${avalues[pos]}" "$cur"
}

_clyops_value___CLYOPS_FUNC__() {
    local kind="$1" values="$2" cur="$3" dir IFS=$'\n'
    case "$kind" in
        choice) COMPREPLY=($(IFS=$' \t\n'; compgen -W "${values//,/ }" -- "$cur")) ;;
        file|dir)
            local flag=-f
            [[ "$kind" == dir ]] && flag=-d
            compopt -o filenames 2>/dev/null
            COMPREPLY=($(compgen $flag -- "$cur"))
            # Bare names also match entries in the option's search dirs.
            if [[ -n "$values" && "$cur" != /* && "$cur" != ./* && "$cur" != ../* ]]; then
                local -a dirs
                IFS=: read -ra dirs <<< "$values"
                for dir in "${dirs[@]}"; do
                    [[ -d "$dir" ]] && COMPREPLY+=($(cd "$dir" && compgen $flag -- "$cur"))
                done
            fi ;;
        host) COMPREPLY=($(compgen -A hostname -- "$cur")) ;;
        none) COMPREPLY=() ;;
        *) compopt -o default 2>/dev/null; COMPREPLY=() ;;
    esac
}

complete -F _clyops___CLYOPS_FUNC__ __CLYOPS_PROG__
CLYOPS_BASH_TEMPLATE
ZSH_COMPLETION =
<<'CLYOPS_ZSH_TEMPLATE'
#compdef __CLYOPS_PROG__
# zsh completion for __CLYOPS_PROG__ (generated by clyops)
# Enable with:  eval "$(__CLYOPS_PROG__ --completion zsh)"
#   or install:  __CLYOPS_PROG__ --completion zsh > "${fpath[1]}/___CLYOPS_PROG__"
_clyops___CLYOPS_FUNC__() {
    local data line esc action skip=0
    local -a f specs cmds
    # The words before the cursor let a dispatcher answer for the subcommand being typed.
    data=$("${words[1]}" --bash-completion -- "${(@)words[2,CURRENT-1]}" 2>/dev/null) || return 1
    [[ "$data" == "#clyops-completion 1"* ]] || return 1

    for line in "${(@f)data}"; do
        [[ "$line" == \#* ]] && continue
        f=("${(@ps:\t:)line}")
        if [[ "$f[1]" == skip ]]; then
            skip=$f[2]
        elif [[ "$f[1]" == cmd ]]; then
            cmds+=("${f[2]//:/\\:}:$f[3]")
        elif [[ "$f[1]" == opt ]]; then
            esc="${${${${f[7]//\\/\\\\}//\[/\\[}//\]/\\]}//:/\\:}"
            if [[ "$f[4]" == flag ]]; then
                specs+=("$f[2][$esc]")
                [[ "$f[3]" != - ]] && specs+=("$f[3][$esc]")
                continue
            fi
            action="$(_clyops_action___CLYOPS_FUNC__ "$f[5]" "$f[6]")"
            specs+=("$f[2]=[$esc]:value:$action")
            [[ "$f[3]" != - ]] && specs+=("$f[3]+[$esc]:value:$action")
        elif [[ "$f[1]" == arg ]]; then
            esc="${${f[6]//\\/\\\\}//:/\\:}"
            action="$(_clyops_action___CLYOPS_FUNC__ "$f[4]" "$f[5]")"
            if [[ "$f[3]" == variadic ]]; then specs+=("*:$esc:$action"); else specs+=(":$esc:$action"); fi
        fi
    done
    # Past a dispatched subcommand, complete its words as if they were the whole line.
    if (( skip )); then
        words=("${words[1]}" "${(@)words[2+skip,-1]}")
        (( CURRENT -= skip ))
    fi
    # A dispatcher group: complete its subcommands.
    if (( ${#cmds} )) && [[ "${words[CURRENT]}" != -* ]]; then
        _describe -t commands command cmds
        return
    fi
    # A dispatched program without completion data: fall back to file names.
    if (( skip && ${#specs} == 0 )); then
        _files
        return
    fi
    _arguments -s -S : "${specs[@]}"
}

_clyops_action___CLYOPS_FUNC__() {
    local kind="$1" values="$2"
    [[ "$values" == - ]] && values=""
    case "$kind" in
        choice) print -r -- "(${values//,/ })" ;;
        file) if [[ -n "$values" ]]; then print -r -- "{_files; _files -W \"(${values//:/ })\"}"; else print -r -- _files; fi ;;
        dir) if [[ -n "$values" ]]; then print -r -- "{_files -/; _files -/ -W \"(${values//:/ })\"}"; else print -r -- "_files -/"; fi ;;
        host) print -r -- _hosts ;;
        none) print -r -- " " ;;
        *) print -r -- _files ;;
    esac
}

if [[ "${zsh_eval_context[-1]}" == loadautofunc ]]; then
    _clyops___CLYOPS_FUNC__ "$@"
else
    compdef _clyops___CLYOPS_FUNC__ __CLYOPS_PROG__
fi
CLYOPS_ZSH_TEMPLATE
FISH_COMPLETION =
<<'CLYOPS_FISH_TEMPLATE'
# fish completion for __CLYOPS_PROG__ (generated by clyops)
# Enable with:  __CLYOPS_PROG__ --completion fish | source
#   or install:  __CLYOPS_PROG__ --completion fish > ~/.config/fish/completions/__CLYOPS_PROG__.fish
function __clyops___CLYOPS_FUNC___value --argument-names kind values cur
    switch $kind
        case choice
            string split , -- $values
        case file
            __fish_complete_path $cur
            # Bare names also match entries in the option's search dirs.
            if test -n "$values"; and not string match -qr -- '^(/|\./|\.\./)' $cur
                for dir in (string split : -- $values)
                    for f in $dir/$cur*
                        string replace -- "$dir/" '' $f
                    end
                end
            end
        case dir
            __fish_complete_directories $cur
        case host
            __fish_print_hostnames
        case none
        case '*'
            __fish_complete_path $cur
    end
end

function __clyops___CLYOPS_FUNC___complete
    set -l tokens (commandline -opc)
    set -l cur (commandline -ct)
    # The words before the cursor let a dispatcher answer for the subcommand being typed.
    set -l data (command $tokens[1] --bash-completion -- $tokens[2..-1] 2>/dev/null)
    string match -q '#clyops-completion 1' -- $data[1]; or return

    set -l skip 0
    set -l cmds
    set -l onames; set -l otypes; set -l okinds; set -l ovalues; set -l odescs
    set -l akinds; set -l avalues; set -l aarity
    for line in $data[2..-1]
        set -l f (string split \t -- $line)
        switch $f[1]
            case skip
                set skip $f[2]
            case cmd
                set -a cmds "$f[2]"\t"$f[3]"
            case opt
                # "-" marks an empty field.
                set -l values $f[6]
                test "$values" = -; and set values ''
                for n in $f[2] $f[3]
                    test "$n" = -; and continue
                    set -a onames $n; set -a otypes $f[4]; set -a okinds $f[5]; set -a ovalues "$values"; set -a odescs "$f[7]"
                end
            case arg
                set -l values $f[5]
                test "$values" = -; and set values ''
                set -a aarity $f[3]; set -a akinds $f[4]; set -a avalues "$values"
        end
    end
    # Words up to and including a dispatched subcommand belong to the dispatcher.
    set -l words $tokens[(math $skip + 2)..-1]

    # --opt=VALUE
    if string match -qr -- '^--[^=]+=' $cur
        set -l opt (string replace -r '=.*' '' -- $cur)
        set -l i (contains -i -- $opt $onames)
        if test -n "$i"; and test "$otypes[$i]" = value
            for v in (__clyops___CLYOPS_FUNC___value $okinds[$i] "$ovalues[$i]" (string replace -r '^[^=]*=' '' -- $cur))
                echo "$opt=$v"
            end
        end
        return
    end
    # --opt VALUE
    if test (count $words) -gt 0
        set -l i (contains -i -- $words[-1] $onames)
        if test -n "$i"; and test "$otypes[$i]" = value
            __clyops___CLYOPS_FUNC___value $okinds[$i] "$ovalues[$i]" $cur
            return
        end
    end
    if string match -q -- '-*' $cur
        for i in (seq (count $onames))
            echo $onames[$i]\t$odescs[$i]
        end
        return
    end
    # A dispatcher group: complete its subcommands.
    if test (count $cmds) -gt 0
        printf '%s\n' $cmds
        return
    end
    # A dispatched program without completion data: fall back to file names.
    if test $skip -gt 0; and test (count $onames) -eq 0; and test (count $akinds) -eq 0
        __fish_complete_path $cur
        return
    end
    # Positional: count earlier positionals, skipping options and their values.
    set -l pos 0
    set -l skipnext 0
    set -l endopts 0
    for w in $words
        if test $skipnext -eq 1
            set skipnext 0
        else if test $endopts -eq 0; and test "$w" = --
            set endopts 1
        else if test $endopts -eq 0; and string match -q -- '-?*' $w
            set -l i (contains -i -- $w $onames)
            test -n "$i"; and test "$otypes[$i]" = value; and set skipnext 1
        else
            set pos (math $pos + 1)
        end
    end
    set -l n (count $akinds)
    test $n -eq 0; and return
    if test $pos -ge $n
        test "$aarity[$n]" = variadic; or return
        set pos (math $n - 1)
    end
    set -l k (math $pos + 1)
    __clyops___CLYOPS_FUNC___value $akinds[$k] "$avalues[$k]" $cur
end

complete -c __CLYOPS_PROG__ -f -a '(__clyops___CLYOPS_FUNC___complete)'
CLYOPS_FISH_TEMPLATE
COMPLETION_SCRIPTS =
{ "bash" => BASH_COMPLETION, "zsh" => ZSH_COMPLETION, "fish" => FISH_COMPLETION }.freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.silent=(value) ⇒ Object (writeonly)

Suppress info/warn/error/success output (die and parse errors still print).



35
36
37
# File 'lib/clyops.rb', line 35

def silent=(value)
  @silent = value
end

Class Method Details

.bool_word(value) ⇒ Boolean?

Convert a supported boolean word, or return nil when unknown.

Parameters:

  • value (Object) —

    The value value.

Returns:

  • (Boolean, nil) —

    the documented result.



104
105
106
107
108
109
# File 'lib/clyops.rb', line 104

def bool_word(value)
  case value.downcase
  when "true", "yes", "1", "on" then true
  when "false", "no", "0", "off" then false
  end
end

.bounds(rule) ⇒ Array<String>

Split a parameterized range rule into lower and upper text bounds.

Parameters:

  • rule (String) —

    Validation rule.

Returns:

  • (Array<String>) —

    the documented result.



133
134
135
136
# File 'lib/clyops.rb', line 133

def bounds(rule)
  lo, _, hi = rule.split(":", 2)[1].partition("-")
  [lo, hi]
end

.completion_kind(rule, search_dirs) ⇒ Array<String>

Return completion kind and values for the validation rule.

Parameters:

  • rule (String) —

    Validation rule.

  • search_dirs (Array<String>) —

    The search dirs value.

Returns:

  • (Array<String>) —

    the documented result.



323
324
325
326
327
328
329
330
331
# File 'lib/clyops.rb', line 323

def completion_kind(rule, search_dirs)
  return ["file", search_dirs.join(":")] if rule == "path" || rule.start_with?("file:")
  return ["dir", search_dirs.join(":")] if rule.start_with?("dir:")
  return ["choice", rule[7..]] if rule.start_with?("choice:")
  return ["choice", "true,false"] if rule == "bool"
  return ["host", ""] if %w[hostname ip].include?(rule)

  rule.empty? ? ["default", ""] : ["none", ""]
end

.describe_rule(rule) ⇒ String

Help text for a validation rule (spec section 5).

Parameters:

  • rule (String) —

    Validation rule.

Returns:

  • (String) —

    the documented result.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/clyops.rb', line 148

def describe_rule(rule)
  return FIXED_RULES[rule] if FIXED_RULES.key?(rule)

  [["int:", "integer", ""], ["float:", "number", ""], ["string:", "text", " chars"]].each do |prefix, noun, suffix|
    next unless rule.start_with?(prefix)
    return "#{noun}: #{rule[prefix.length..]}#{suffix}" unless rule.include?("-")

    lo, hi = bounds(rule)
    return "#{noun}: #{lo}-#{hi}#{suffix}" if !lo.empty? && !hi.empty?
    return lo.empty? ? "#{noun}: <=#{hi}#{suffix}" : "#{noun}: >=#{lo}#{suffix}"
  end
  return "choices: #{rule[7..].split(',', -1).join(', ')}" if rule.start_with?("choice:")
  return "pattern: #{rule[6..]}" if rule.start_with?("regex:")

  rule
end

.die(code, msg) ⇒ void

This method returns an undefined value.

Print an error and exit with ‘code`. Never suppressed.

Parameters:

  • code (Integer) —

    The code value.

  • msg (String) —

    The msg value.



70
71
72
73
# File 'lib/clyops.rb', line 70

def die(code, msg)
  emit("error", msg, force: true)
  exit(code)
end

.emit(level, msg, force: false) ⇒ Integer?

Write a timestamped log message; force bypasses the silent setting.

Parameters:

  • level (String) —

    The level value.

  • msg (String) —

    The msg value.

  • force (Boolean) (defaults to: false) —

    The force value.

Returns:

  • (Integer, nil) —

    bytes written, or nil when suppressed.



42
43
44
45
46
47
# File 'lib/clyops.rb', line 42

def emit(level, msg, force: false)
  return if @silent && !force

  tag = $stderr.tty? && !ENV["NO_COLOR"] ? "#{COLORS[level]}#{level}\e[0m" : level
  $stderr.write("#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} [#{tag}] #{msg}\n")
end

.error(msg) ⇒ Integer?

Write an error to stderr.

Parameters:

  • msg (String) —

    The msg value.

Returns:

  • (Integer, nil) —

    bytes written, or nil when suppressed.



60
61
62
63
# File 'lib/clyops.rb', line 60

def error(msg) = emit("error", msg)
# Write a success message to stderr.
# @param msg [String] The msg value.
# @return [Integer, nil] bytes written, or nil when suppressed.

.info(msg) ⇒ Integer?

Write an informational message to stderr.

Parameters:

  • msg (String) —

    The msg value.

Returns:

  • (Integer, nil) —

    bytes written, or nil when suppressed.



52
53
54
55
# File 'lib/clyops.rb', line 52

def info(msg) = emit("info", msg)
# Write a warning to stderr.
# @param msg [String] The msg value.
# @return [Integer, nil] bytes written, or nil when suppressed.

.known_rule?(rule) ⇒ Boolean

Whether the validation rule is supported.

Parameters:

  • rule (String) —

    Validation rule.

Returns:

  • (Boolean) —

    the documented result.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/clyops.rb', line 114

def known_rule?(rule)
  return true if rule.empty? || FIXED_RULES.key?(rule)
  return true if rule.match?(/\Aint:(\d+-\d*|-\d+)\z/) || rule.match?(/\Afloat:(\d*\.?\d+-(\d*\.?\d+)?|-\d*\.?\d+)\z/)
  return true if rule.match?(/\Astring:(\d+|\d+-\d*|-\d+)\z/) || rule.match?(/\Achoice:.+\z/m)

  if rule.start_with?("regex:") && rule.length > 6
    begin
      Regexp.new(rule[6..])
      return true
    rescue RegexpError
      return false
    end
  end
  false
end

.normpath(base, value) ⇒ String

Join and normalize like Python’s os.path.normpath(os.path.join(…)): an absolute ‘value` replaces `base`.

Parameters:

  • base (String) —

    The base value.

  • value (Object) —

    The value value.

Returns:

  • (String) —

    the documented result.



262
263
264
# File 'lib/clyops.rb', line 262

def normpath(base, value)
  Pathname.new(base).join(value).cleanpath.to_s
end

.path_rule?(rule) ⇒ Boolean

Whether the rule denotes a path, file, or directory.

Parameters:

  • rule (String) —

    Validation rule.

Returns:

  • (Boolean) —

    the documented result.



141
142
143
# File 'lib/clyops.rb', line 141

def path_rule?(rule)
  rule == "path" || rule.start_with?("file:", "dir:")
end

.pretty_json(value) ⇒ String

JSON with two-space indents and empty arrays and objects written as

and {}, whatever the json gem version. A raw newline only appears

between JSON tokens (in strings it is escaped), so this can’t touch values.

Parameters:

  • value (Object) —

    The value value.

Returns:

  • (String) —

    the documented result.



315
316
317
# File 'lib/clyops.rb', line 315

def pretty_json(value)
  JSON.pretty_generate(value).gsub(/\[\n\s*\]/, "[]").gsub(/\{\n\s*\}/, "{}")
end

.resolve_path(value, base, search_dirs = []) ⇒ String

Resolve a path value against ‘base` (spec section 6).

Parameters:

  • value (Object) —

    The value value.

  • base (String) —

    The base value.

  • search_dirs (Array<String>) (defaults to: []) —

    The search dirs value.

Returns:

  • (String) —

    the documented result.



271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/clyops.rb', line 271

def resolve_path(value, base, search_dirs = [])
  return value if value.empty? || %w[- disabled optional].include?(value)
  return value if value.start_with?("/") || value.match?(/\A[A-Za-z][A-Za-z0-9+.-]+:/)

  from_base = normpath(base, value)
  if !value.match?(%r{\A\.\.?(/|\z)}) && !search_dirs.empty? && !File.exist?(from_base)
    search_dirs.each do |dir|
      candidate = normpath(dir, value)
      return candidate if File.exist?(candidate)
    end
  end
  from_base
end

.success(msg) ⇒ Integer?

Write a success message to stderr.

Parameters:

  • msg (String) —

    The msg value.

Returns:

  • (Integer, nil) —

    bytes written, or nil when suppressed.



64
# File 'lib/clyops.rb', line 64

def success(msg) = emit("success", msg)

.validate(value, rule, name) ⇒ String, ...

Validate ‘value` against `rule` and return its typed form; raises ValidationError with the spec’s error text.

Parameters:

  • value (Object) —

    The value value.

  • rule (String) —

    Validation rule.

  • name (String) —

    Name identifying the value, command, or program.

Returns:

  • (String, Integer, Float, Boolean) —

    the documented result.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/clyops.rb', line 171

def validate(value, rule, name)
  fail_with = ->(msg) { raise ValidationError, "#{name} #{msg}" }
  check_bounds = lambda do |num|
    lo, hi = bounds(rule)
    fail_with.("must be >= #{lo}, got #{value}") if !lo.empty? && num < lo.to_f
    fail_with.("must be <= #{hi}, got #{value}") if !hi.empty? && num > hi.to_f
  end

  if rule == "int" || rule.start_with?("int:")
    fail_with.("must be an integer, got '#{value}'") unless value.match?(/\A-?[0-9]+\z/)
    num = Integer(value, 10)
    check_bounds.(num) unless rule == "int"
    return num
  end
  if rule == "float" || rule.start_with?("float:")
    fail_with.("must be a number, got '#{value}'") unless value.match?(/\A-?[0-9]*\.?[0-9]+\z/)
    num = Float(value.sub(/\A(-?)\./, '\10.'))
    check_bounds.(num) unless rule == "float"
    return num
  end
  if rule.start_with?("string:")
    length = value.length
    if rule.include?("-")
      lo, hi = bounds(rule)
      fail_with.("must be at least #{lo} characters, got #{length}") if !lo.empty? && length < lo.to_i
      fail_with.("must be at most #{hi} characters, got #{length}") if !hi.empty? && length > hi.to_i
    elsif length != rule[7..].to_i
      fail_with.("must be exactly #{rule[7..]} characters, got #{length}")
    end
    return value
  end
  if rule.start_with?("choice:")
    choices = rule[7..].split(",", -1)
    fail_with.("must be one of: #{choices.join(', ')}, got '#{value}'") unless choices.include?(value)
    return value
  end
  if rule.start_with?("regex:")
    fail_with.("does not match required pattern, got '#{value}'") unless value.match?(Regexp.new(rule[6..]))
    return value
  end

  case rule
  when "bool"
    b = bool_word(value)
    fail_with.("must be a boolean (true/false, yes/no, 1/0, on/off), got '#{value}'") if b.nil?
    return b
  when "port"
    unless value.match?(/\A[0-9]+\z/) && (1..65_535).cover?(value.to_i)
      fail_with.("must be a valid port (1-65535), got '#{value}'")
    end
    return value.to_i
  when "ip"
    unless value.match?(/\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/) || value.match?(/\A([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}\z/)
      fail_with.("must be a valid IP address, got '#{value}'")
    end
  when "hostname"
    fail_with.("must be a valid hostname, got '#{value}'") unless value.match?(HOSTNAME)
  when "url"
    fail_with.("must be a valid URL, got '#{value}'") unless value.match?(%r{\Ahttps?://[a-zA-Z0-9.-]+(:[0-9]+)?(/.*)?\z}m)
  when "email"
    fail_with.("must be a valid email address, got '#{value}'") unless value.match?(/\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/)
  when "uuid"
    unless value.match?(/\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/)
      fail_with.("must be a valid UUID, got '#{value}'")
    end
  when "date:YYYY-MM-DD"
    fail_with.("must be in YYYY-MM-DD format, got '#{value}'") unless value.match?(/\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z/)
  when "file:exists"
    fail_with.("file does not exist: #{value}") unless File.file?(value)
  when "file:readable"
    fail_with.("file is not readable: #{value}") unless File.readable?(value)
  when "file:writable"
    if File.exist?(value) || File.symlink?(value)
      fail_with.("file is not writable: #{value}") unless File.writable?(value)
    else
      dir = File.dirname(value)
      fail_with.("directory is not writable: #{dir}") unless File.directory?(dir) && File.writable?(dir)
    end
  when "dir:exists"
    fail_with.("directory does not exist: #{value}") unless File.directory?(value)
  when "dir:writable"
    fail_with.("directory does not exist or is not writable: #{value}") unless File.directory?(value) && File.writable?(value)
  end
  value
end

.warn(msg) ⇒ Integer?

Write a warning to stderr.

Parameters:

  • msg (String) —

    The msg value.

Returns:

  • (Integer, nil) —

    bytes written, or nil when suppressed.



56
57
58
59
# File 'lib/clyops.rb', line 56

def warn(msg) = emit("warning", msg)
# Write an error to stderr.
# @param msg [String] The msg value.
# @return [Integer, nil] bytes written, or nil when suppressed.

.wrap_text(text, width) ⇒ Array<String>

Greedy word wrap that keeps existing line breaks (spec section 7).

Parameters:

  • text (String) —

    The text value.

  • width (Integer) —

    The width value.

Returns:

  • (Array<String>) —

    the documented result.



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/clyops.rb', line 289

def wrap_text(text, width)
  text.split(/\r?\n/, -1).flat_map do |original|
    words = original.split
    next [""] if words.empty?

    out = []
    line = +""
    words.each do |word|
      if line.empty?
        line = +word
      elsif line.length + 1 + word.length <= width
        line << " " << word
      else
        out << line
        line = +word
      end
    end
    out << line
  end
end