# fancyopts.py - better command line parsing
#
#  Copyright 2005-2009 Matt Mackall <[email protected]> and others
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.

import getopt

def gnugetopt(args, options, longoptions):
   """Parse options mostly like getopt.gnu_getopt.

   This is different from getopt.gnu_getopt in that an argument of - will
   become an argument of - instead of vanishing completely.
   """
   extraargs = []
   if '--' in args:
       stopindex = args.index('--')
       extraargs = args[stopindex+1:]
       args = args[:stopindex]
   opts, parseargs = getopt.getopt(args, options, longoptions)
   args = []
   while parseargs:
       arg = parseargs.pop(0)
       if arg and arg[0] == '-' and len(arg) > 1:
           parseargs.insert(0, arg)
           topts, newparseargs = getopt.getopt(parseargs, options, longoptions)
           opts = opts + topts
           parseargs = newparseargs
       else:
           args.append(arg)
   args.extend(extraargs)
   return opts, args


def fancyopts(args, options, state, gnu=False):
   """
   read args, parse options, and store options in state

   each option is a tuple of:

     short option or ''
     long option
     default value
     description

   option types include:

     boolean or none - option sets variable in state to true
     string - parameter string is stored in state
     list - parameter string is added to a list
     integer - parameter strings is stored as int
     function - call function with parameter

   non-option args are returned
   """
   namelist = []
   shortlist = ''
   argmap = {}
   defmap = {}

   for short, name, default, comment in options:
       # convert opts to getopt format
       oname = name
       name = name.replace('-', '_')

       argmap['-' + short] = argmap['--' + oname] = name
       defmap[name] = default

       # copy defaults to state
       if isinstance(default, list):
           state[name] = default[:]
       elif hasattr(default, '__call__'):
           state[name] = None
       else:
           state[name] = default

       # does it take a parameter?
       if not (default is None or default is True or default is False):
           if short: short += ':'
           if oname: oname += '='
       if short:
           shortlist += short
       if name:
           namelist.append(oname)

   # parse arguments
   if gnu:
       parse = gnugetopt
   else:
       parse = getopt.getopt
   opts, args = parse(args, shortlist, namelist)

   # transfer result to state
   for opt, val in opts:
       name = argmap[opt]
       t = type(defmap[name])
       if t is type(fancyopts):
           state[name] = defmap[name](val)
       elif t is type(1):
           state[name] = int(val)
       elif t is type(''):
           state[name] = val
       elif t is type([]):
           state[name].append(val)
       elif t is type(None) or t is type(False):
           state[name] = True

   # return unparsed args
   return args