4.2.3. GNATCOLL.Config

package GNATCOLL.Config is

   --------------------------
   -- Parsing config files --
   --------------------------

   type Config_Parser is abstract tagged private;
   --  Abstract type for all config streams (files, in-memory,...), with any
   --  format. Concrete types below will provide the actual implementation.

   --  Typical usage looks like:

   --     declare
   --        C : File_Config_Parser;
   --     begin
   --        Open (C, "filename.txt");
   --        while not C.At_End loop
   --           Put_Line (C.Key & " = " & C.Value);
   --           C.Next;
   --        end loop;
   --     end;

   function At_End (Self : Config_Parser) return Boolean is abstract;
   --  Whether the config parsing is at the end

   procedure Next (Self : in out Config_Parser) is abstract;
   --  Move to the next (key, value) in the configuration. Before that call,
   --  the parser is left on the first value in the configuration.

   function Section (Self : Config_Parser) return String is abstract;
   function Key (Self : Config_Parser) return String is abstract;
   function Value (Self : Config_Parser) return String is abstract;
   --  Return the current (section, key, value);

   procedure Set_System_Id (Self : in out Config_Parser; System_ID : String);
   --  Sets the system ID for the config.
   --  If the config is found in a file, this should be the absolute path name
   --  to that file. This will generally be called automatically when opening
   --  the file.
   --  This system id is used to resolve absolute file names.

   function As_Integer       (Self : Config_Parser) return Integer;
   function As_Boolean       (Self : Config_Parser) return Boolean;
   function As_Absolute_File (Self : Config_Parser) return String;
   function As_Absolute_Dir  (Self : Config_Parser) return String;
   --  Assuming the current value is a file or directory, converts it to an
   --  absolute name, where relative paths are resolved relative to the
   --  config's system_id.
   --  These will raise Constraint_Error if used on non-matching values.

   -----------------
   -- File config --
   -----------------

   type File_Config_Parser is abstract new Config_Parser with private;
   --  A special implementation for config streams based on actual files

   procedure Open (Self : in out File_Config_Parser; Filename : String);
   --  Open a file

   overriding function At_End (Self : File_Config_Parser) return Boolean;

   ---------------
   -- INI files --
   ---------------

   type INI_Parser is new File_Config_Parser with private;
   --  a special parser for Windows' .ini files

   procedure Configure
     (Self             : in out INI_Parser;
      Comment_Start    : String := "#";
      Handles_Sections : Boolean := True;
      Home             : String := "");
   --  "Home" is the substitution pattern for "HOME" in the values. If
   --  unspecified, it is computed automatically.

   overriding procedure Open (Self : in out INI_Parser; Filename : String);
   overriding procedure Next (Self : in out INI_Parser);
   overriding function Section (Self : INI_Parser) return String;
   overriding function Key (Self : INI_Parser) return String;
   overriding function Value (Self : INI_Parser) return String;

   -------------------
   -- Resource pool --
   -------------------

   type Config_Pool is tagged private;
   --  This type provides storage for a config file

   procedure Set_System_Id (Self : in out Config_Pool; System_ID : String);
   --  Set the absolute name used to resolve file names in Get_File

   procedure Fill
     (Self   : in out Config_Pool;
      Config : in out Config_Parser'Class);
   --  Load all keys from Config, and store the (key, value) pairs in Self.
   --  Multiple files can be merged into the same pool.
   --  Set_System_Id is automatically called, thus file names will be resolved
   --  relative to the last Config loaded in the pool.

   Section_From_Key : constant String;
   --  Indicates that the section should in fact be read from the key (as
   --  opposed to being specified separately). In this case, the key is split
   --  at the first "." (if there is none, the section name is empty).
   --  For instance: "section1.key1" or "section1.key2".
   --
   --  It is often more convenient to specify the section that way, in exchange
   --  for a small performance penalty and a possible ambiguity if the key
   --  itself contains a ".", which is not recommended.

   Whole_Value : constant Natural := 0;

   function Get (Self    : Config_Pool;
                 Key     : String;
                 Section : String := Section_From_Key;
                 Index   : Natural := Whole_Value) return String;
   --  Return the value associated with Key.
   --  Index is used for comma-separated lists of values, and will retrieve
   --  one of the specific elements of the list. The whole value (no splitting)
   --  is returned if Index is Whole_Value. The empty string is returned if
   --  there is no such item in the list

   function Get_Integer
     (Self    : Config_Pool;
      Key     : String;
      Section : String := Section_From_Key;
      Index   : Natural := Whole_Value) return Integer;

   function Get_Boolean
     (Self    : Config_Pool;
      Key     : String;
      Section : String := Section_From_Key;
      Index   : Natural := Whole_Value) return Boolean;

   function Get_File
     (Self    : Config_Pool;
      Key     : String;
      Section : String := Section_From_Key;
      Index   : Natural := Whole_Value) return String;
   --  Same as above, but returns an absolute filename. Relative paths are
   --  resolved relative to the config location where Key was declared.

   function To_File
     (Self    : Config_Pool;
      Key     : String;
      Section : String := Section_From_Key;
      Value   : String) return GNATCOLL.VFS.Virtual_File;
   --  Converts value to a file. It is relative to the location of the config
   --  file that provided Key. This is similar to calling Get_File directly,
   --  but is useful in contexts where you need to first manipulate the value
   --  read from the config and then interpret it as a file.

   procedure Set (Self : in out Config_Pool; Section, Key, Value : String);
   --  Override a specific key

   --------------------------------
   -- Resource pool, static keys --
   --------------------------------

   type Config_Key is tagged private;

   function Create (Key : String; Section : String := "") return Config_Key;
   --  Create a new config key

   function Get
     (Self  : Config_Key;
      Conf  : Config_Pool'Class;
      Index : Natural := Whole_Value) return String;
   function Get_Integer
     (Self  : Config_Key;
      Conf  : Config_Pool'Class;
      Index : Natural := Whole_Value) return Integer;
   function Get_Boolean
     (Self  : Config_Key;
      Conf  : Config_Pool'Class;
      Index : Natural := Whole_Value) return Boolean;
   function Get_File
     (Self  : Config_Key;
      Conf  : Config_Pool'Class;
      Index : Natural := Whole_Value) return String;
   function To_File
     (Self  : Config_Key;
      Conf  : Config_Pool'Class;
      Value : String) return GNATCOLL.VFS.Virtual_File;
   --  Read the key from the configuration.
   --  Using this API might help ensure that you are always accessing existing
   --  keys. In this case, you would have a global package that defines all
   --  valid keys:
   --
   --      Key1 : constant Config_Key := Create ("...");
   --      Key2 : constant Config_Key := Create ("...");
   --
   --  Then your coding standard should specify that you can only access the
   --  configuration via those keys:
   --
   --      Put_Line (Key1.Get);
   --
   --  There is therefore no possible typo in the name of the key, and if you
   --  rename the key in the configuration file, you have a single place to
   --  change.

end GNATCOLL.Config;