require "colorize" require "yaml" class Baguette::Context # By default, just print everything. class_property verbosity = 4 end class Baguette::Configuration class_property project_directory : String? = nil class Base include YAML::Serializable # Check for $XDG_CONFIG_HOME/baguette//.yml, # then /etc/baguette//.yml. # (project name is not mandatory) def self.get(file : String? = nil) filename = "/" + (self.name.downcase.gsub /baguette::configuration::/, "") + ".yml" directory = "/baguette" if pdir = Baguette::Configuration.project_directory directory += "/" + pdir end suffix = "#{directory}#{filename}" user_configuration = (ENV["XDG_CONFIG_HOME"]? || "#{ENV["HOME"]}/.config") + suffix system_configuration = "/etc#{suffix}" if ! file.nil? && ! File.exists? file.not_nil! Baguette::Log.warning "Provided configuration file does not exist: #{file}" end if ! file.nil? && File.exists? file.not_nil! Baguette::Log.info "Using configuration file: #{file}" self.from_yaml File.read(file.not_nil!) elsif File.exists? user_configuration Baguette::Log.info "Using user configuration: #{user_configuration}" self.from_yaml File.read(user_configuration) elsif File.exists? system_configuration Baguette::Log.info "Using system configuration: #{system_configuration}" self.from_yaml File.read(system_configuration) else Baguette::Log.warning "No configuration found" Baguette::Log.warning "Searched in: #{user_configuration}" Baguette::Log.warning "Searched in: #{system_configuration}" nil end end end class IPC < Base enum MESSAGE TIMER CONNECTION DISCONNECTION EXTERNAL # extra socket RX TX SWITCH ERROR EXCEPTION end property ipc_timer : Int32 = 30_000 # 30 seconds. property ipc_messages_to_show : Array(IPC::MESSAGE) = [MESSAGE::ERROR, MESSAGE::EXCEPTION] property verbosity : Int32 = 4 def initialize end end # Read options from the CLI. # We currently want to know: # - the program verbosity # - the configuration directory to use (project name) # - if the configuration files should be ignored # - if this is just a simulation (used to print configuration then quit) def self.option_parser simulation = false no_configuration = false configuration_file = nil help = false OptionParser.parse do |parser| parser.banner = "usage: #{PROGRAM_NAME} [-ns][--project project-name]" parser.on "-s", "--simulation", "Print configuration then quit." do simulation = true end parser.on "-n", "--no-configuration", "No configuration file should be read." do no_configuration = true end parser.on "--configuration file", "Configuration file to use." do |f| configuration_file = f end parser.on "-v verbosity", "--verbosity level", "Verbosity level. From 0 to 4. Default: 4" do |v| Baguette::Context.verbosity = v.to_i end parser.on "--project project-name", "Project name. Will search in $XDG_CONFIG_HOME/baguette//.yml then /etc/baguette//.yml." do |dir| Baguette::Configuration.project_directory = dir end parser.invalid_option do |arg| # Do not print anything: we only check for configuration file stuff. end parser.on "-h", "--help", "Show this help" do puts parser help = true end end # Options are removed once read from the ARGV array, but we want to propagate # this particular option to print the second set of options. ARGV.push "-h" if help return simulation, no_configuration, configuration_file end end # `Baguette::Log` is a library to display information. # A filename can be given to write logs in a file. class Baguette::Log # FIXME: def log(), that puts stuff as-is in the logs. # Name of the file. class_property log_path : String? = nil enum LOGTYPE DEBUG = 5 INFO = 4 TITLE = 3 WARNING = 2 ERROR = 1 end def self.now() : String Time.local.to_s("%Y-%m-%d_%H:%M:%S") end def self.write_log_fancy(io : IO, logtype : LOGTYPE, text) case logtype when .debug? io.<<(self.now).<<(" :: ".colorize(:cyan)).<<(text.colorize(:cyan)).<<("\n") when .info? io.<<(self.now).<<(" :: ".colorize(:blue)).<<(text.colorize(:white)).<<("\n") when .title? io.<<(self.now).<<(" |> ".colorize(:blue).bright).<<(text.colorize(:white).bright).<<("\n") when .warning? io.<<(self.now).<<(" :: ".colorize(:yellow).bright).<<(text.colorize(:yellow)).<<("\n") when .error? io.<<(self.now).<<(" !! ".colorize(:red).bright).<<(text.colorize(:red)).<<("\n") end end def self.write_log_plain(io : IO, logtype : LOGTYPE, text) case logtype when .debug? io.<<(self.now).<<(" (debug) ").<<(text).<<("\n") when .info? io.<<(self.now).<<(" (info) ").<<(text).<<("\n") when .title? io.<<(self.now).<<(" (title) ").<<(text).<<("\n") when .warning? io.<<(self.now).<<(" (warning) ").<<(text).<<("\n") when .error? io.<<(self.now).<<(" (error) ").<<(text).<<("\n") end end def self.write_log(logtype : LOGTYPE, text) if path = @@log_path File.open path, "a" do |io| self.write_log_plain io, logtype, text end # In case the thing we want to print is important, put it on the screen too. if logtype <= LOGTYPE::WARNING self.write_log_fancy STDERR, logtype, text end else if logtype <= LOGTYPE::WARNING self.write_log_fancy STDERR, logtype, text else self.write_log_fancy STDOUT, logtype, text end end end def self.debug(text) return unless Baguette::Context.verbosity > 3 self.write_log LOGTYPE::DEBUG, text end def self.info(text) return unless Baguette::Context.verbosity > 2 self.write_log LOGTYPE::INFO, text end def self.title(text) return unless Baguette::Context.verbosity > 2 self.write_log LOGTYPE::TITLE, text end def self.warning(text) return unless Baguette::Context.verbosity > 1 self.write_log LOGTYPE::WARNING, text end def self.error(text) return unless Baguette::Context.verbosity > 0 self.write_log LOGTYPE::ERROR, text end end