Configuration options for nit tools and apps

This module provides basic services for options handling in your Nit programs.

Basic configuration holder

The Config class can be used as a simple option holder and processor:

import config

# Create a new config option
var opt_my = new OptionString("My option", "--my")

# Create the config and add the option
var config = new Config
config.add_option(opt_my)

# Parse the program arguments, usually `args`
config.parse_options(["--my", "myOption", "arg1", "arg2"])

# Access the options and args
assert opt_my.value == "myOption"
assert config.args == ["arg1", "arg2"]

Custom configuration class

Instead of using basic Config instances, it is better to define new sublcasses to store options and define custom services.

import config

class MyConfig
    super Config

    var opt_my = new OptionString("My option", "--my")

    init do
        super
        tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
        add_option(opt_my)
    end

    fun my: String do return opt_my.value or else "Default value"
end

var config = new MyConfig
config.parse_options(["--my", "myOption", "arg1", "arg2"])

assert config.my == "myOption"
assert config.args == ["arg1", "arg2"]

We define the my method to provide an elegant shortcut to opt_my.value and define the default value if the option was not set by the user.

The tool_description attribute is used to set the usage header printed when the user request the help message.

config.parse_options(["-h"])
if config.help then
    config.usage
    exit 0
end

This will display the tool usage like this:

Usage: MyExample [OPTION]... [ARGS]...
 -h, --help   Show this help message
 --my         My option

Configuration with ini file

The IniConfig class extends Config to add an easy way to link your configuration to an ini file.

class MyIniConfig
    super IniConfig

    var opt_my = new OptionString("My option", "--my")

    init do
        super
        tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
        opts.add_option(opt_my)
    end

    fun my: String do return opt_my.value or else ini["my"] or else "Default"
end

This time, we define the my method to return the option value or the ini if no option was passed. Finally, if no ini value can be found, we return the default value.

By default, IniConfig looks at a config.ini file in the execution directory. This can be overrided in multiple ways.

First by the app user by setting the --config option:

var config = new MyIniConfig
config.parse_options(["--config", "my_config.ini"])

assert config.config_file == "my_config.ini"

Default config file can also be changed by the library client through the default_config_file attribute:

config = new MyIniConfig
config.default_config_file = "my_config.ini"
config.parse_options(["arg"])

assert config.config_file == "my_config.ini"

Or by the library developper in the custom config class:

class MyCustomIniConfig
    super IniConfig

    redef var default_config_file = "my_config.ini"
end

var config = new MyCustomIniConfig
config.parse_options(["arg"])

assert config.config_file == "my_config.ini"

Introduced classes

class Config

config :: Config

Basic configuration class
class IniConfig

config :: IniConfig

Configuration class based on a INI file.

All class definitions

class Config

config $ Config

Basic configuration class
class IniConfig

config $ IniConfig

Configuration class based on a INI file.
package_diagram config::config config ini ini config::config->ini opts opts config::config->opts core core ini->core opts->core ...core ... ...core->core popcorn::pop_config pop_config popcorn::pop_config->config::config markdown::nitmd nitmd markdown::nitmd->config::config markdown2::nitmd nitmd markdown2::nitmd->config::config nlp::nlp_index nlp_index nlp::nlp_index->config::config nlp::nlp_server nlp_server nlp::nlp_server->config::config vsm::example_vsm example_vsm vsm::example_vsm->config::config popcorn::pop_repos pop_repos popcorn::pop_repos->popcorn::pop_config popcorn::pop_repos... ... popcorn::pop_repos...->popcorn::pop_repos a_star-m a_star-m a_star-m->markdown::nitmd a_star-m->markdown2::nitmd a_star-m->nlp::nlp_index a_star-m->nlp::nlp_server a_star-m->vsm::example_vsm a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module math

core :: math

Mathematical operations
module native

core :: native

Native structures for text and bytes
module numeric

core :: numeric

Advanced services for Numeric types
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O

Parents

module ini

ini :: ini

Read and write INI configuration files
module opts

opts :: opts

Management of options on the command line

Children

module example_vsm

vsm :: example_vsm

Example using a FileIndex
module nitmd

markdown :: nitmd

A Markdown parser for Nit.
module nitmd

markdown2 :: nitmd

A Markdown parser for Nit.
module nlp_index

nlp :: nlp_index

Example showing how to use a NLPFileIndex.
module pop_config

popcorn :: pop_config

Configuration file and options for Popcorn apps

Descendants

module a_star-m

a_star-m

module loader

github :: loader

module pop_repos

popcorn :: pop_repos

Repositories for data management.
# Configuration options for nit tools and apps
#
# This module provides basic services for options handling in your Nit programs.
#
# ## Basic configuration holder
#
# The `Config` class can be used as a simple option holder and processor:
#
# ~~~
# import config
#
# # Create a new config option
# var opt_my = new OptionString("My option", "--my")
#
# # Create the config and add the option
# var config = new Config
# config.add_option(opt_my)
#
# # Parse the program arguments, usually `args`
# config.parse_options(["--my", "myOption", "arg1", "arg2"])
#
# # Access the options and args
# assert opt_my.value == "myOption"
# assert config.args == ["arg1", "arg2"]
# ~~~
#
# ## Custom configuration class
#
# Instead of using basic `Config` instances, it is better to define new sublcasses
# to store options and define custom services.
#
# ~~~
# import config
#
# class MyConfig
#	super Config
#
#	var opt_my = new OptionString("My option", "--my")
#
#	init do
#		super
#		tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
#		add_option(opt_my)
#	end
#
#	fun my: String do return opt_my.value or else "Default value"
# end
#
# var config = new MyConfig
# config.parse_options(["--my", "myOption", "arg1", "arg2"])
#
# assert config.my == "myOption"
# assert config.args == ["arg1", "arg2"]
# ~~~
#
# We define the `my` method to provide an elegant shortcut to `opt_my.value`
# and define the default value if the option was not set by the user.
#
# The `tool_description` attribute is used to set the `usage` header printed when
# the user request the `help` message.
#
# ~~~
# config.parse_options(["-h"])
# if config.help then
#	config.usage
#	exit 0
# end
# ~~~
#
# This will display the tool usage like this:
#
# ~~~raw
# Usage: MyExample [OPTION]... [ARGS]...
#  -h, --help   Show this help message
#  --my         My option
# ~~~
#
# ## Configuration with `ini` file
#
# The `IniConfig` class extends `Config` to add an easy way to link your
# configuration to an ini file.
#
# ~~~
# class MyIniConfig
#	super IniConfig
#
#	var opt_my = new OptionString("My option", "--my")
#
#	init do
#		super
#		tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
#		opts.add_option(opt_my)
#	end
#
#	fun my: String do return opt_my.value or else ini["my"] or else "Default"
# end
# ~~~
#
# This time, we define the `my` method to return the option value or the ini
# if no option was passed. Finally, if no ini value can be found, we return the
# default value.
#
# By default, `IniConfig` looks at a `config.ini` file in the execution directory.
# This can be overrided in multiple ways.
#
# First by the app user by setting the `--config` option:
#
# ~~~
# var config = new MyIniConfig
# config.parse_options(["--config", "my_config.ini"])
#
# assert config.config_file == "my_config.ini"
# ~~~
#
# Default config file can also be changed by the library client through the
# `default_config_file` attribute:
#
# ~~~
# config = new MyIniConfig
# config.default_config_file = "my_config.ini"
# config.parse_options(["arg"])
#
# assert config.config_file == "my_config.ini"
# ~~~
#
# Or by the library developper in the custom config class:
#
# ~~~
# class MyCustomIniConfig
#	super IniConfig
#
#	redef var default_config_file = "my_config.ini"
# end
#
# var config = new MyCustomIniConfig
# config.parse_options(["arg"])
#
# assert config.config_file == "my_config.ini"
# ~~~
module config

import ini
import opts

# Basic configuration class
#
# ~~~
# import config
#
# class MyConfig
#	super Config
#
#	var opt_my = new OptionString("My option", "--my")
#
#	init do
#		super
#		tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
#		opts.add_option(opt_my)
#	end
#
#	fun my: String do return opt_my.value or else "Default value"
# end
#
# var config = new MyConfig
# config.parse_options(["--my", "hello", "arg1", "arg2"])
# assert config.my == "hello"
# assert config.args == ["arg1", "arg2"]
# ~~~
class Config

	# Context used to store and parse options
	var opts = new OptionContext

	# Help option
	var opt_help = new OptionBool("Show this help message", "-h", "-?", "--help")

	# Option --stub-man
	var opt_stub_man = new OptionBool("Generate a stub manpage in markdown format", "--stub-man")

	# Redefine this init to add your options
	init do
		add_option(opt_help, opt_stub_man)
		opt_stub_man.hidden = true
	end

	# Add an option to `self`
	#
	# Shortcut to `opts.add_option`.
	fun add_option(opt: Option...) do opts.add_option(opt...)

	# Initialize `self` options from `args`
	fun parse_options(args: Collection[String]) do
		opts.parse(args)

		if opt_stub_man.value then
			stub_man_options
			exit 0
		end
	end

	# Return the remaining args once options are parsed by `from_args`
	fun args: Array[String] do return opts.rest

	# Name, usage and synopsis of the tool.
	# It is mainly used in `usage`.
	# Should be correctly set by the client before calling `usage`
	# A multi-line string is recommended.
	#
	# eg. `"Usage: tool [OPTION]... [FILE]...\nDo some things."`
	var tool_description: String = "Usage: [OPTION]... [ARG]..." is writable

	# Was the `--help` option requested?
	fun help: Bool do return opt_help.value

	# Display `tool_description` and options usage in console
	fun usage do
		print tool_description
		print "\nOptions:"
		opts.usage
	end

	# Generate a manpage stub from `self`
	fun stub_man_options do
		var lines = tool_description.split("\n")
		var name = sys.program_name.basename
		var syn = lines.shift
		print "# NAME"
		print ""
		if lines.not_empty then
			printn "{name} - "
			print lines.join("\n")
			print ""
		end
		print "# SYNOPSIS"
		print ""
		print syn.replace("Usage: ", "")
		print ""
		print "# OPTIONS"
		for o in opts.options do
			if o.hidden then continue

			var first = true
			print ""
			printn "### "
			for n in o.names do
				if first then first = false else printn ", "
				printn "`{n}`"
			end
			print ""
			print "{o.helptext}."
		end
		exit 0
	end
end

# Configuration class based on a INI file.
#
# ~~~
# class MyIniConfig
#	super IniConfig
#
#	var opt_my = new OptionString("My option", "--my")
#
#	init do
#		super
#		tool_description = "Usage: MyExample [OPTION]... [ARGS]..."
#		opts.add_option(opt_my)
#	end
#
#	fun my: String do return opt_my.value or else ini["my"] or else "Default"
# end
#
# var config = new MyIniConfig
# config.default_config_file = "my_config.ini"
# config.parse_options(args)
#
# if config.help then
#	config.usage
#	exit 0
# end
#
# assert config.my == "Default"
# ~~~
class IniConfig
	super Config

	# Config tree used to store config options
	var ini: IniFile is noinit

	# Path to app config file
	var opt_config = new OptionString("Path to config file", "--config")

	init do
		super
		opts.add_option(opt_config)
	end

	redef fun parse_options(args) do
		super
		ini = new IniFile.from_file(config_file)
	end

	# Default config file path
	var default_config_file = "config.ini" is writable

	# Return the config file path from options or the default
	fun config_file: String do return opt_config.value or else default_config_file
end
lib/config/config.nit:15,1--322,3