# -*- encoding: UTF-8 -*- require 'optparse' require 'optparse/time' require 'ostruct' require 'csd/extensions' module CSD # A class that handles the command line option parsing and manipulation # class OptionsParser < OpenStruct def parse! clear parse_literals define_actions_and_scopes parse_options end def valid_action? public_actions = actions['public'] ? self.actions['public'].map { |pair| pair.keys.first } : [] developer_actions = actions['developer'] ? self.actions['developer'].map { |pair| pair.keys.first } : [] all_actions = public_actions + developer_actions all_actions.include?(self.action) end def define_actions_and_scopes if Applications.current # Here we overwrite the default supported actions and scopes with the application specific ones UI.debug "#{self.class} loads the actions of #{Applications.current} now" self.actions = Applications.current.actions # At this point we know that the first argument is no option, but *some* action (may it be valid or not) UI.debug "#{self.class} loads the scopes of #{Applications.current} now" self.scopes = Applications.current.scopes(self.action) end end def clear # Resetting all attributes to nil (because e.g. an application instance might have modified or added some). super # First we define all valid actions and scopes self.actions = [] self.scopes = [] # Then we define the default literals self.help = false self.application = nil self.action = nil # Now we define the default options self.yes = false self.local = false self.reveal = false self.verbose = false self.silent = false self.developer = false # Shortcut to allow for debugging the options parser itself self.debug = (ARGV.include?('--debug') or ARGV.include?('-d')) end # Here we check for literals, i.e. "help", ACTION and APPLICATION. # def parse_literals # First let's see whether we are in help mode, i.e. whether the first argument is `help´. # If so, we would like to remove it from the ARGV list. if ARGV.first == 'help' self.help = true ARGV.shift end # The action and the application name are the other literals we're interested in at this point. # Note: If there is no application specified, there is pretty much nothing we can do for the user. if Applications.current and Applications.current.name == ARGV.first # The application name is the first argument (i.e. there is no action specified at all) # Let's store the application name and remove it from the argument line self.application = ARGV.shift elsif Applications.current and Applications.current.name == ARGV.second # The second argument is the application name. This means that the first argument must be the # action or happens to be some option. In case it's no option, lets take it as desired action. unless ARGV.first.starts_with?('-') self.action = ARGV.shift self.application = ARGV.shift # Removing the application name from the argument list end end end # Parse all options that the user gave as command parameter. Note that this function strips the options # from ARGV and leaves only literal (non-option) parameters (i.e. actions/applications/scopes; strings without -- and -). # def parse_options OptionParser.new do |opts| self.banner = Applications.current ? 'OPTIONS' : "Usage: ".bold + "#{CSD.executable} [help] [TASK] APPLICATION [OPTIONS]" opts.banner = self.banner.magenta.bold # Here we load application-specific options file. # TODO: There must be a better way for this in general than to eval the raw ruby code begin unless Applications.current.options(self.action).size == 0 opts.headline "#{self.action.to_s.upcase} #{Applications.current.name.upcase} OPTIONS".green.bold eval Applications.current.options(self.action) else UI.debug "There were no options to be loaded from #{Applications.current}" end rescue SyntaxError => e raise Error::Application::OptionsSyntax, "The individual options of #{Applications.current.inspect} could not be parsed (SyntaxError)." end if Applications.current # And here we load all general options options_prepend = Applications.current ? 'GENERAL ' : nil opts.headline "#{options_prepend}OPTIONS".green.bold opts.on("-y", "--yes", "Answer all questions with `yes´ (batch mode)") do |value| self.yes = value end #opts.on("-l", "--local","Assume that there is no uplink to the Internet") do |value| # self.online = !value #end opts.on("-r", "--reveal","List all commands that normally would be executed in this operation (preview-mode)") do |value| self.reveal = value end opts.on("-e", "--verbose","Show more elaborate output") do |value| self.verbose = value end opts.on("-d", "--debug","Show more elaborate output and debugging information about the AI") do |value| self.debug = value end opts.on("-s", "--silent","Don't show any output") do |value| self.silent = value end opts.on_tail("-a", "--developer", "Show information only relevant to AI and MiniSIP developers") do |value| self.developer = value end opts.on_tail("-h", "--help", "Show detailed help (regarding the given ACTION and APPLICATION)") do |value| self.help = value end opts.on_tail("-v", "--version", "Show the version of this AI") do puts "CSD Gem Version: #{CSD::Version}".blue raise Error::Argument::VersionWasRequested end self.helptext = opts.help end.parse! rescue OptionParser::InvalidOption => e raise Error::Argument::InvalidOption, e.message.gsub('invalid option: ', 'This option argument seems to be incorrect: ') end end end