require "file_utils" require "ini" module Funfriend::ConfigMan extend self APPLICATION_NAME = "funfriend" CONFIG_NAME = "cfg.ini" alias ConfigValue = String | Int32 | Bool | Float64 alias ConfigSection = Hash(String, ConfigValue) alias Config = Hash(String, ConfigSection) DEFAULT_CONFIG = { "window" => { "funfriend_size" => 75.as(ConfigValue) }, "sound" => { "volume" => 0.2.as(ConfigValue) }, "buddies" => { "types" => "funfriend".as(ConfigValue) }, }.as(Config) def get_config_path : Path {% if flag?(:windows) %} Path.new(ENV["APPDATA"]) / APPLICATION_NAME {% elsif flag?(:macosx) %} Path.home / "Library" / "Application Support" / APPLCATION_NAME {% elsif flag?(:unix) %} (ENV["XDG_CONFIG_HOME"]?.try { |p| Path.new(p) } || (Path.home / ".config")) / APPLICATION_NAME {% end %} end @@config_initialized = false @@config = {} of String => ConfigSection def config if !@@config_initialized raise "config read before initialization" end @@config end def init config_path = get_config_path FileUtils.mkdir_p(config_path) config_file = config_path / CONFIG_NAME if !File.exists?(config_file) @@config = DEFAULT_CONFIG.dup File.write(config_file, INI.build(@@config)) else parsed = INI.parse(File.read(config_file)) @@config = DEFAULT_CONFIG.dup parsed.each do |sect_i, section| section.each do |key, value| # if it exists in the default config if DEFAULT_CONFIG[sect_i]?.try &.[key]? # try casting casted_val = case DEFAULT_CONFIG[sect_i][key] when String value when Int32 value.to_i32? when Float64 value.to_f64? when Bool value === "1" || value.downcase === "true" end # casted_val being nil means it failed; fall back to default if casted_val != nil # write to config @@config[sect_i][key] = casted_val.not_nil! end else # keep as a string just incase it's ever needed @@config[sect_i][key] = value end end end File.write(config_file, INI.build(@@config)) end @@config_initialized = true end end