with Ada.Wide_Wide_Text_IO; with Ada.Directories; with AAA.Strings; with AAA.Directories; with CLIC.Config.Load; with Simple_Logging; with TOML; package body CLIC.Config.Edit is package Trace renames Simple_Logging; use TOML; procedure Write_Config_File (Table : TOML_Value; Path : String) with Pre => Table.Kind = TOML_Table; function Remove_From_Table (Table : TOML_Value; Key : Config_Key; Quiet : Boolean := False) return Boolean with Pre => Table.Kind = TOML_Table; -- When Quiet, don't print error messages when returning False function Add_In_Table (Table : TOML_Value; Key : Config_Key; Val : TOML_Value) return Boolean with Pre => Table.Kind = TOML_Table; ----------------------- -- Write_Config_File -- ----------------------- procedure Write_Config_File (Table : TOML_Value; Path : String) is use Ada.Wide_Wide_Text_IO; use Ada.Directories; File : File_Type; begin -- Create the directory for the config file, in case it doesn't exists Create_Path (Containing_Directory (Path)); Create (File, Out_File, Path); Trace.Debug ("Write config: '" & TOML.Dump_As_String (Table) & "'"); Put (File, WW (TOML.Dump_As_String (Table))); Close (File); end Write_Config_File; ----------------------- -- Remove_From_Table -- ----------------------- function Remove_From_Table (Table : TOML_Value; Key : Config_Key; Quiet : Boolean := False) return Boolean is use AAA.Strings; Id : constant String := Split (Key, '.', Raises => False); Leaf : constant Boolean := Id = Key; begin if not Table.Has (Id) then -- The key doesn't exist if not Quiet then Trace.Error ("Configuration key not defined"); end if; return False; end if; if Leaf then Table.Unset (Id); return True; else declare Sub : constant TOML_Value := Table.Get (Id); begin if Sub.Kind = TOML_Table then return Remove_From_Table (Sub, Split (Key, '.', Tail)); else if not Quiet then Trace.Error ("Configuration key not defined"); end if; return False; end if; end; end if; end Remove_From_Table; ------------------ -- Add_In_Table -- ------------------ function Add_In_Table (Table : TOML_Value; Key : Config_Key; Val : TOML_Value) return Boolean is use AAA.Strings; Id : constant String := Split (Key, '.', Raises => False); Leaf : constant Boolean := Id = Key; begin if Leaf then Table.Set (Id, Val); return True; end if; if not Table.Has (Id) then -- The subkey doesn't exist, create a table for it Table.Set (Id, Create_Table); end if; declare Sub : constant TOML_Value := Table.Get (Id); begin if Sub.Kind = TOML_Table then return Add_In_Table (Sub, Split (Key, '.', Tail), Val); else Trace.Error ("Configuration key already defined"); return False; end if; end; end Add_In_Table; ----------- -- Unset -- ----------- function Unset (Path : String; Key : Config_Key; Quiet : Boolean := False) return Boolean is use AAA.Directories; Tmp : Replacer := New_Replacement (File => Path, Backup => False, Allow_No_Original => True); Table : constant TOML_Value := Load.Load_TOML_File (Tmp.Editable_Name); begin if Table.Is_Null then -- The configuration file doesn't exist or is not valid if not Quiet then Trace.Error ("configuration file doesn't exist or is not valid"); end if; return False; end if; if not Remove_From_Table (Table, Key, Quiet) then return False; end if; Write_Config_File (Table, Tmp.Editable_Name); Tmp.Replace; return True; end Unset; --------- -- Set -- --------- function Set (Path : String; Key : Config_Key; Value : String; Check : Check_Import := null) return Boolean is use AAA.Directories; Tmp : Replacer := New_Replacement (File => Path, Backup => False, Allow_No_Original => True); Table : TOML_Value := Load.Load_TOML_File (Tmp.Editable_Name); To_Add : constant TOML_Value := To_TOML_Value (Value); begin if To_Add.Is_Null then Trace.Error ("Invalid configuration value: '" & Value & "'"); return False; end if; if Check /= null and then not Check (Key, To_Add) then return False; end if; if Table.Is_Null then -- The configuration file doesn't exist or is not valid. Create an -- empty table. Table := TOML.Create_Table; end if; if not Add_In_Table (Table, Key, To_Add) then return False; end if; Write_Config_File (Table, Tmp.Editable_Name); Tmp.Replace; return True; end Set; --------------- -- Set_Typed -- --------------- function Set_Typed (Path : String; Key : Config_Key; Value : Value_Type; Check : Check_Import := null) return Boolean is ------------------ -- Check_Proper -- ------------------ -- Check that the type to be stored is actually the one intended, in -- addition to the user check function Check_Proper (Key : Config_Key; Value : TOML.TOML_Value) return Boolean is ((Check = null or else Check (Key, Value)) and then Value.Kind = TOML_Type); ----------------- -- Set_Boolean -- ----------------- function Set_Boolean return Boolean is begin return Set (Path, Key, (case Boolean'Value (Image (Value)) is when True => "true", when False => "false"), Check_Proper'Unrestricted_Access); end Set_Boolean; begin if TOML_Type = TOML_Boolean then -- Boolean is special because we need to pass exactly "true/false" return Set_Boolean; else return Set (Path, Key, Image (Value), Check_Proper'Unrestricted_Access); end if; end Set_Typed; end CLIC.Config.Edit;