#  -*- text -*-
#
#
#  $Id: 362fda22ed882b2679de2ec2ac5a23f32e27526c $

#######################################################################
#
#  = CSV Module
#
#  Read CSV files and use them in maps.
#
#  The CSV files MUST be formatted according to `RFC 4180`.
#
#  WARNING: Multi-line fields are NOT allowed.
#
#  The CSV map can be used in a `map` section, as in the following example.
#
#    map csv &User-Name {
#		Attribute-Name := field1
#		Attribute-Name := field2
#		...
#    }
#
#  The argument to "map" is dynamically expanded.  The result is taken
#  as a string, and is used as the value of the "key".  The key is
#  then looked up in the cached CSV file.  The fields are then mapped
#  to the attributes on the left side of the map.
#
#  ## Configuration Settings
#
csv {
	#
	#  filename:: The file which contains the CSV data.
	#
	filename = ${modconfdir}/csv/${.:instance}

	#
	#  delimiter:: The field delimiter. MUST be a one-character string.
	#
	delimiter = ","

	#
	#  header:: Whether or not there is a one-line header in the file.
	#
	#  If the value is set to 'yes', then the CSV file MUST contain
	#  a header as the first line of the file.  That header line
	#  must contain the field names.
	#
	header = no

	#
	#  allow_multiple_keys:: Whether the file can have multiple entries
	#  which match the same key.
	#
	#  The default is `no`.
	#
	#  If set to `yes`, then multiple entries are allowed.  When a
	#  key matches, each entry is applied in the order it appears
	#  in the file.
	#
	allow_multiple_keys = no

	#
	#  fields:: A string which defines field names.
	#
	#  This configuration item is used only when `header = no`.
	#  The content of the `fields` item must be the same as in RFC 4180.
	#  That is, a list of field names, separated by the `delimiter`
	#  character.
	#
	#  The `csv` module can be used to read files such as `/etc/group`
	#  By setting `delimiter = ":"`, and by using `fields = "group:::,user"`
	#
	#  The special character `,` can be used *only* for the `key`
	#  field, and *only* if the `key` field is the last field.
	#  This special syntax means "add multiple entries for this
	#  line, one for each `key`".
	#
	#  [NOTE]
	#  =====
	#  * the header MUST have the same number of fields as are in the CSV file.
	#  * the field names MUST NOT include whitespace.
	#  * Fields which are not used should have no name
	#
	#  e.g. *"foo,,bar"* defines 3 fields, where the second is unused.
	#  =====
	fields = "name,size,color,count"

	#
	#  index_field:: The name of the field which is used to index the
	#  entries.
	#
	#  It can be any one of the field names defined above.
	#
	#  The CSV rows are normally placed into a binary tree,
	#  indexed by this field.  A binary tree allows for fast
	#  lookups, no matter the size of the CSV file.
	#
	#  When looking up entries in the binary tree, the key must match
	#  the `index_field` exactly.
	#
	#  If `data_type` is an IP address type, then the CSV rows are
	#  placed into a prefix trie, indexed by this field.  The
	#  prefix trie allows for fast prefix lookups.
	#
	#  When looking up entries in a prefix trie, the closest
	#  enclosing prefix is matched.  This prefix match allows you
	#  to place `192.0.2/24` as an index field in the file, and
	#  then lookups of `192.0.2.1` will return that row.
	#
	index_field = "name"

	#
	#  key:: The key string used to look up entries via the `index_field`.
	#
	#  When the `csv` module is listed in a processing section,
	#  the `key` is used to find the appropriate entry.  The `update`
	#  section below is then applied.
	#
	#  The data type of the key is used to determine the type
	#  of structure used to store the `index_field` values.
	#
	#  When the key data type is one of: `ipaddr`, `ipv4prefix`,
	#  `ipv6addr`, or `ipv6prefix`, then the rows are stored in a
	#  prefix trie.
	#
	#  For all other key data types, the rows are stored in a binary
	#  tree.
	#
	#  If a data type other than the native type of `key` expression
	#  is needed, the casting operator can be used.
	#  For example:
	#  a key value of `<ipv4prefix>&reply.Reply-Message`.
	#  would result in a prefix trie being used for lookups, and the
	#  `string` value of the `Reply-Message` attribute being parsed as
	#  CIDR notation.
	#
	#  Note that the individual fields of the CSV file do not have
	#  data types.  They are stored internally as strings, and are
	#  parsed to the final data type only when the `csv` module
	#  is run, either in-place, or as a `map`.
	#
	key = &User-Name

	#
	#  ### Mapping of CSV fields to attributes.
	#
	#  WARNING: Although this format is almost identical to the `unlang`
	#  update section format, it does *NOT* mean that you can use other
	#  `unlang` constructs in module configuration files.
	#
	#  Configuration items are in the format:
	#
	#    <fr attr> <op> <csv field>
	#
	#  Where:
	#
	#  [options="header,autowidth"]
	#  |===
	#  | Parameter   | Description
	#  | <fr attr>   | Is the destination RADIUS attribute
	#                  with any valid list and request qualifiers.
	#  | <op>        | Is any assignment attribute (=, :=, +=, -=).
	#  | <csv field> | Is the name of a field from the CSV file, as taken
	#                  from the `fields` configuration item.
	#  |===
	#
	#  Request and list qualifiers may be placed after the `update`
	#  section name to set default destination requests/lists
	#  for `<fr attr>s` with no list qualifiers.
	#
	#  NOTE: CSV field names should be single quoted unless you want
	#  the name to be derived from an xlat expansion, or an attribute ref.
	#
	#  update { ... }::
	#
	update reply {
	       &Reply-Message := 'color'
	       &my-integer := 'count'
	}

	#
	#  The module also exports a `map` expansion, via the syntax:
	#
	#	map cvs <key> { ... }
	#
	#  Where `csv` is the name of the module, and `key` is an expansion
	#  as given the the key` field above.  For example, the map could
	#  look like this:
	#
	#	map csv &User-Name {
	#		&reply.Reply-Message := 'color'
	#		&my-integer := 'count'
	#	}
	#
	#  This map does the same operations as the key / update
	#  fields given above.  The benefit here is that the key can
	#  be dynamically changed, depending on the needs of the
	#  current section.
	#
	#  If the key is not found in the CSV file, then the `map`
	#  does nothing.
	#
}
