Cleanup conditional construction logic for --resolved and --user

Signed-off-by: Joey Armstrong <jarmstrong@linuxfoundation.org>
Change-Id: I2080713f8c340aa234923e0a226030ec1b666133
diff --git a/jira/bin/jira-search.sh b/jira/bin/jira-search.sh
index 9015a38..4710b75 100755
--- a/jira/bin/jira-search.sh
+++ b/jira/bin/jira-search.sh
@@ -331,21 +331,47 @@
 ## --------------------------------------------------------------------
 function do_user()
 {
-    local -n ans=$1; shift
+    local -n ref=$1; shift
 
     [[ -v argv_nobody ]] && return
 
+    local -a buffer=()
     local user='currentUser()'
-    if [[ -v argv_user ]]; then
-        user="$argv_user"
-    fi
+    [[ ! -v argv_users ]] && { declare -a argv_users=('currentUser()'); }
+    
+    local users
+    users="$(join_by ',' "${argv_users[@]}")"
 
     if [[ -v argv_assigned ]]; then
-        ans+=("assignee=${user}")
+        if [[ ${#argv_users[@]} -eq 1 ]]; then
+            buffer+=("assignee=${user}")
+        else
+            buffer+=("(asignee in ($users))")
+        fi
     fi
 
     if [[ -v argv_reported ]]; then
-        ans+=("reporter=${user}")
+        if [[ ${#argv_users[@]} -eq 1 ]]; then
+            buffer+=("reporter=${user}")
+        else
+            buffer+=("(reporter in ($users))")
+        fi
+    fi
+
+    ## --------------------------
+    ## Construct resolved queries
+    ## -------------------------- 
+    local query="$(join_by ' OR ' "${buffer[@]}")"
+
+    ## ----------------------------------------
+    ## Normalize parens for the composite query
+    ## ----------------------------------------
+    if [[ ${#ref[@]} -eq 0 ]]; then
+        ref+=("$query")
+    else
+        local buffer
+        buffer="$(join_by ' AND ' "(${ref[@]})" "($query)")"
+        ref=("$buffer")
     fi
 
     return
@@ -468,7 +494,6 @@
   --in                 Include ticket field if list item(s) match
   --not                Negate a query
   --is-empty           Include ticket if field is empty
-  --resolved-is-empty
   --unresolved         Query for open/unresolved jira tickets.
 
 [FILTER]
@@ -482,6 +507,7 @@
   --resolved(-not)-empty         Query for (closed)/open tickets
   --resolved-excl                Types to exclude
   --resolved-incl                Types to include
+  --resolved-in {start} {end}    Helps resolve join({and,or}) ambiguity
 
 [RANGE]
   --newer [d]      Search for tickets created < [n] days ago.
@@ -497,6 +523,11 @@
   --resolved       Query by field: resolved
   --user           Query by owner, requestor or 'my' jira tickets.
 
+[USER]
+  --user [u]       Login of user to query for
+  --assigned       Query for tickets assigned to the user
+  --reported       Query for tickets reported by the user
+
 [HELP]
   --help           This message
   --help-{topic}   Display switch help and use case.     (--help-resolved)
@@ -537,35 +568,41 @@
 
 # declare -g -i debug=1
 
+unset attrs
 while [ $# -gt 0 ]; do
 
     arg="$1"; shift
     banner "ARG=[$arg], \$@=[$@]"
-    
+
     [[ -v debug ]] && echo "** argv=[$arg] [$*]"
 
     getopt_detect_modifiers "$arg"
 
-    case "$arg" in
+    if getopts_switch__not arg; then
+        echo "DETECTED: NOT"
+        declare -i modifier_found=1
+    elif getopts_switch__empty arg; then
+        echo "DETECTED: EMPTY"
+        declare -i modifier_found=1
+    else
+        declare -i modifier_found=0
+    fi
 
+    if [[ $modifier_found -eq 1 ]]; then
+        echo "*ATTRS MODIFIER (LINENO:$LINENO): $(declare -p attrs)"
+        declare -a args=()
+        [[ ${#arg} -gt 0 ]] && { args+=("$arg"); }
+        [[ $# -gt 0 ]] && { args+=("$@"); }
+        set -- "${args[@]}"
+        continue
+    fi
+
+    case "$arg" in        
         '--help')    usage; exit 0                      ;;
         '--help-'*)  help_with "${arg/--help-/}"        ;;
         '--usage-'*) help_usage_show "${arg/--usage-/}" ;;
  
-        *'-is-empty'*)
-            is_switch_valid__is_empty "$arg"
-
-            declare -a args=()
-            arg="${arg:2}"               # remove prefix --
-            arg="${arg%-is-empty}"       # remove suffix token-name
-
-            ## Rewrite switch to bypass is-empty and match --required
-            args+=("--${arg}+is-empty")  # ARGV: gen JCL tokens
-            [[ $# -gt 0 ]] && { args+=("$@"); }
-
-            set -- "${args[@]}" # --is-empty resolved
-            ;;
-
+            
         ##-----------------##
         ##---]  MODES  [---##
         ##-----------------##
@@ -575,7 +612,7 @@
         ##------------------------##
         ##---]  SWITCH ALIAS  [---##
         ##------------------------##
-        --unresolved|--wip)
+        '--unresolved'|'--wip')
             declare -a args=()
             args+=('--resolved-is-empty')
             [[ $# -gt 0 ]] && { args+=("$@"); }
@@ -585,13 +622,25 @@
         ##-------------------##
         ##---]  BY USER  [---##
         ##-------------------##
-        --assigned) declare -g -i argv_assigned=1 ;;
-        --reported) declare -g -i argv_reported=1 ;;
-        --me)       declare -g -i argv_me=1       ;;
+        '--assigned') declare -g -i argv_assigned=1 ;;
+        '--reported') declare -g -i argv_reported=1 ;;
+        '--me')
+            declare -a args=()
+            args+=('--user' 'currentUser()')
+            args+=('--assigned')
+            args+=('--reported')
+            [[ $# -gt 0 ]] && { args+=("$@"); }
+            set -- "${args[@]}"
+            ;;
+        
         --nobody)   declare -g -i argv_nobody=1   ;;
-        --user)
+        '--user')
             arg="$1"; shift
             declare -g argv_user="$arg"
+
+            [[ ! -v argv_users ]] && { declare -g -a argv_users=(); }
+            argv_users+=("$arg")
+            declare -p argv_users
             ;;
 
         ##------------------##
@@ -690,32 +739,33 @@
             ;;
 
         '--resolved'*) # [-+]
+
+            gen_attrs__empty 'resolved'
+            
+            # [[ -v resolved_not_empty ]] && { declare -p resolved_not_empty; }
+            # [[ -v resolved_is_empty ]]  && { declare -p resolved_is_empty; }
+
             # function get_jql_reasons()
             case "$arg" in
+                '--resolved') ;;
 
-                *start) declare -g resolved_start="$1"; shift ;;
-                *end) declare -g resolved_end="$1";   shift ;;
+                *'start') declare -g resolved_start="$1"; shift ;;
+                *'end')   declare -g resolved_end="$1";   shift ;;
 
-                *not-empty) declare -g resolved_not_empty="$1" ;;
-                *'empty')
-                    banner "Detected [$arg]"
-                    declare -g resolved_is_empty="$1"
-                    ;;
-
-                *excl)
+                *'excl')
                     [[ ! -v resolved_excl ]] && { declare -g -a resolved_excl=(); }
                     val="\"$1\""; shift
                     html_encode val
                     resolved_excl+=("$val");
                     ;;
-                *incl)
+                *'incl')
                     [[ ! -v resolved_incl ]] && { declare -g -a resolved_incl=(); }
                     val="\"$1\""; shift
                     html_encode val
                     resolved_incl+=("$val");
                     ;;
-                *) ;;
-                *) error "Detected invalid --resolved-* modifier" ;;
+
+                *) error "Detected invalid --resolved-* modifier arg=[$arg]" ;;
             esac
             ;;
 
@@ -797,6 +847,11 @@
             ;;
     esac
 
+
+    if [[ -v resolved ]]; then
+        clear_attrs__empty
+    fi
+
 done
 
 ## --------------
@@ -812,25 +867,22 @@
 [[ -v components ]] && { do_components components suffix0; }
 do_labels labels_incl labels_excl suffix0
 do_text  text                     suffix0
-
 get_jcl_resolved                  suffix0
-    # do_resolved                       suffix0
+
 
 do_fixversion                     suffix0
 
-declare -p getopt_argv_any_OR
+[[ -v getopt_argv_any_OR ]] && { declare -p getopt_argv_any_OR; }
+
 if [[ -v getopt_argv_any_OR ]]; then
-    query="$(join_by 'OR' "${suffix0[0]}")"
+    query="$(join_by ' OR ' "${suffix0[0]}")"
 elif [[ -v getopt_argv_any_AND ]]; then
-    query="$(join_by 'AND' "${suffix0[0]}")"
+    query="$(join_by ' AND ' "${suffix0[0]}")"
 else
-#    query="$(join_by 'OR' "${suffix0[0]}")"
-    error "Ambiguous query [argv needs: --and or --or]"
+#    query="$(join_by ' OR ' "${suffix0[0]}")"
+    error "Ambiguous query [argv needs: --and or --or], argv=[$*]"
 fi
 
-# banner "$(declare -p query)"
-
-
 
 filter=''
 gen_filter filter suffix0
diff --git a/jira/jira-search/include.sh b/jira/jira-search/include.sh
index 8f81512..773afba 100644
--- a/jira/jira-search/include.sh
+++ b/jira/jira-search/include.sh
@@ -48,4 +48,7 @@
 source "${pgm_lib}/resolved.sh"
 source "${pgm_lib}/help/utils.sh"
 
+source "${pgm_lib}/is_empty/include.sh"
+source "${pgm_lib}/is_not/include.sh"
+
 # [EOF]
diff --git a/jira/jira-search/is_empty.sh b/jira/jira-search/is_empty.sh
index fe6e9d1..5abe08a 100644
--- a/jira/jira-search/is_empty.sh
+++ b/jira/jira-search/is_empty.sh
@@ -21,7 +21,7 @@
     if [[ " ${valid[@]} " =~ " ${val} " ]]; then
         is_empty+=("$val")
     else
-        error "Detected invalid --is-empty switch [$arg]"
+        error "Detected invalid --empty switch [$arg]"
     fi
     
     return
diff --git a/jira/jira-search/is_empty/include.sh b/jira/jira-search/is_empty/include.sh
new file mode 100644
index 0000000..0acb0c2
--- /dev/null
+++ b/jira/jira-search/is_empty/include.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+## -----------------------------------------------------------------------
+## Intent: getopts parsing and set flag status for --is-empty swtiches
+## -----------------------------------------------------------------------
+
+## -----------------------------------------------------------------------
+## Intent: Parse command line switch, when *-empty detected take action
+##   - remove is-empty OR is-not-empty from swtich string
+##   - set values in map %attrs=() to capture detection bits
+## Return:
+##   %attrs
+##    --is-empty        empty=true
+##    --is-not-empty    empty=true, not=true
+## -----------------------------------------------------------------------
+function getopts_switch__empty()
+{ 
+    local -n gse_arg=$1; shift
+    declare -g -A attrs
+
+    case "$gse_arg" in
+        *'-is-empty')
+            is_switch_valid__is_empty "$gse_arg"
+            attrs['empty']=1
+            # declare -g -i argv_is_empty=1
+            gse_arg="${gse_arg/-is-empty/}"
+            local -i rc=0
+            ;;
+        *) local -i rc=1 ;;
+    esac
+
+    [[ $rc -eq 0 ]] && { true; } || { false; }
+    return
+}
+
+## -----------------------------------------------------------------------
+## Intent: Define flags based on detected switch modifiers
+## -----------------------------------------------------------------------
+function gen_attrs__empty()
+{
+    local switch="$1"; shift
+    declare -g attrs
+
+    local is="${switch}_is_empty"
+    local not="${switch}_not_empty"
+    declare -g -i "$is"
+    declare -g -i "$not"
+    
+    ## Clear prior state
+    [[ -v "$is" ]]  && { echo "IS"; unset "$is"; }
+    [[ -v "$not" ]] && { echo "NOT"; unset "$not"; }
+
+    ## Apply current attributes
+    ## [TODO] parameterize flag creation
+    if [[ ! -v attrs['empty'] ]]; then
+        : # fall through
+    elif [[ -v attrs['not'] ]]; then
+        declare -g -i $not=1
+    else
+        declare -g -i $is=1
+    fi
+
+    : # assign ($?==0)
+    return
+}
+
+
+# [EOF]
diff --git a/jira/jira-search/is_not/include.sh b/jira/jira-search/is_not/include.sh
new file mode 100644
index 0000000..0858b36
--- /dev/null
+++ b/jira/jira-search/is_not/include.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
+
+## -----------------------------------------------------------------------
+## Intent: Parse command line switch, when *-not detected take action
+##   - remove substring '-not' modifier from switch name
+##   - set values in map %attrs=() to capture detection of NOT
+##   - switch value returned modified
+## Return:
+##   %attrs   ['not']=true
+## -----------------------------------------------------------------------
+function getopts_switch__not()
+{
+    local -n gsn_arg=$1 ; shift
+    declare -g -A attrs
+
+    case "$gsn_arg" in
+
+        # -[-not]
+        '--not')
+            attrs['not']=1
+            gsn_argv=''
+            local -i rc=0
+            ;;
+
+        # --reserved-is[-not]-empty
+        *'-not'*)
+            declare -a args=()
+            attrs['not']=1
+            gsn_arg="${gsn_arg/-not/}"
+            local -i rc=0
+            ;;
+
+        *) local -i rc=1 ;;
+    esac
+
+    [[ $rc -eq 0 ]] && { true; } || { false; }
+    return
+}
+
+# [EOF]
diff --git a/jira/jira-search/resolved.sh b/jira/jira-search/resolved.sh
index 88b6835..cf6bf31 100644
--- a/jira/jira-search/resolved.sh
+++ b/jira/jira-search/resolved.sh
@@ -10,13 +10,26 @@
 function get_jcl_resolved()
 {
     local -n ref=$1; shift
+    local -i count=${#ref[@]}
 
     local -a tmp=()
     do_resolved tmp
-    ref+=("$tmp")
-    
-    local query="$(join_by 'OR' "${tmp[0]}")"
-    ref+=("($query)")
+
+    ## --------------------------
+    ## Construct resolved queries
+    ## -------------------------- 
+    local query="$(join_by ' OR ' "${tmp[@]}")"
+
+    ## ----------------------------------------
+    ## Normalize parens for the composite query
+    ## ----------------------------------------
+    if [[ ${#ref[@]} -eq 0 ]]; then
+        ref+=("$query")
+    else
+        local buffer
+        buffer="$(join_by ' AND ' "(${ref[@]})" "($query)")"
+        ref=("$buffer")
+    fi
     
     return
 }
@@ -44,12 +57,23 @@
 {
     declare -n ans=$1; shift
 
-    [[ -v resolved_start ]] && { ans+=("(Resolved >= $resolved_start)"); }
-    [[ -v resolved_end ]]   && { ans+=("(Resolved <= $resolved_end)"); }
+    # --resolved-in {start} {end} can resolve join({and,or}) ambiguity
+    # [[ -v resolved_start ]] && { ans+=("(Resolved >= $resolved_start)"); }
+    # [[ -v resolved_end ]]   && { ans+=("(Resolved <= $resolved_end)"); }
+    declare -a joinby=()
+    [[ -v resolved_start ]] && { joinby+=("(Resolved >= $resolved_start)"); }
+    [[ -v resolved_end ]]   && { joinby+=("(Resolved <= $resolved_end)"); }
+    
+    if [[ ${#joinby[@]} -lt 2 ]]; then
+        ans+=("${joinby[@]}")
+    else
+        filter="$(join_by ' AND ' "${joinby[@]}")"
+        ans+=("(${filter})")
+    fi
 
+    
     if [[ -v resolved_excl ]]; then
         filter="$(join_by ',' "${resolved_excl[@]}")"
-        declare -p filter
         ans+=( "(resolution NOT IN ($filter))" )
     fi
 
@@ -58,14 +82,12 @@
         ans+=( "(resolution IN ($filter))" )
     fi
 
-    [[ -v resolved_not_empty ]] \
-        && { ans+=('(resolved IS NOT EMPTY)'); } \
-        || { true; }
-               
-    [[ -v resolved_is_empty ]] \
-        && { ans+=('(resolved IS EMPTY)'); } \
-        || { true; }
-
+    if [[ -v resolved_not_empty ]]; then
+        ans+=('(resolved IS NOT EMPTY)')
+    elif [[ -v resolved_is_empty ]]; then
+        ans+=('(resolved IS EMPTY)')
+    fi
+    
     return
 }