diff --git a/conf/conf.go b/conf/conf.go new file mode 100644 index 0000000..de0ed0e --- /dev/null +++ b/conf/conf.go @@ -0,0 +1,183 @@ +/* +Copyright 2013 Petru Ciobanu, Francesco Paglia, Lorenzo Pierfederici + +This file is part of maponet/utils. + +maponet/utils is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +maponet/utils is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with maponet/utils. If not, see . +*/ + +/* +Package conf contains a simple loader for configuration files. +*/ +package conf + +import ( + "bufio" + "fmt" + "io" + "os" + "regexp" + "strconv" + "sync" + "bytes" +) + +type NotFoundError struct { + key string +} + +func (e *NotFoundError) Error() string { return fmt.Sprintf("Key not found: %s", e.key) } + +type ConfigSyntaxError struct { + line string +} + +func (e *ConfigSyntaxError) Error() string { + return fmt.Sprintf("Config syntax error on line: %s", e.line) +} + +// Regexp patterns +var ( + PatternOption, _ = regexp.Compile("(.*)=([^#]*)") + PatternComment, _ = regexp.Compile("^#") + PatternEmpty, _ = regexp.Compile("^[\t\n\f\r ]$") +) + +// Config is a simple synchronized object that can be used to parse +// configuration files and store key=value pairs +type Config struct { + values map[string]string + mutex sync.RWMutex +} + +func NewConfig() *Config { + var c Config + c.values = make(map[string]string) + return &c +} + +// Parse +func (c *Config) Parse(r *bufio.Reader) error { + for { + l, err := r.ReadBytes('\n') + if err == io.EOF { + break + } else if err != nil { + return err + } + switch { + case PatternEmpty.Match(l): + continue + case PatternComment.Match(l): + continue + case PatternOption.Match(l): + m := PatternOption.FindSubmatch(l) + c.Set(string(bytes.TrimSpace(m[1])), string(bytes.TrimSpace(m[2]))) + default: + return &ConfigSyntaxError{string(l)} + } + } + return nil +} + +// ParseFile +func (c *Config) ParseFile(path string) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + err = c.Parse(bufio.NewReader(f)) + if err != nil { + return err + } + return nil +} + +// Set +func (c *Config) Set(key, value string) { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.values[key] = value +} + +// GetString +func (c *Config) GetString(key string) (string, error) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + v, ok := c.values[key] + if !ok { + return v, &NotFoundError{key} + } + return v, nil +} + +// GetBool +func (c *Config) GetBool(key string) (bool, error) { + s, err := c.GetString(key) + if err != nil { + return *new(bool), err + } + + return strconv.ParseBool(s) +} + +// GetInt +func (c *Config) GetInt(key string) (int, error) { + s, err := c.GetString(key) + if err != nil { + return *new(int), err + } + + return strconv.Atoi(s) +} + +// GetFloat64 +func (c *Config) GetFloat64(key string) (float64, error) { + s, err := c.GetString(key) + if err != nil { + return *new(float64), err + } + + return strconv.ParseFloat(s, 64) +} + +var DefaultConfig = NewConfig() + +func ParseFile(path string) error { + return DefaultConfig.ParseFile(path) +} + +func Set(key, value string) { + DefaultConfig.Set(key, value) +} + +func GetString(key string) (string, error) { + return DefaultConfig.GetString(key) +} + +func GetBool(key string) (bool, error) { + return DefaultConfig.GetBool(key) +} + +func GetInt(key string) (int, error) { + return DefaultConfig.GetInt(key) +} + +func GetFloat64(key string) (float64, error) { + return DefaultConfig.GetFloat64(key) +} diff --git a/log/log.go b/log/log.go index df36ed5..74b1131 100644 --- a/log/log.go +++ b/log/log.go @@ -23,9 +23,8 @@ Package log contains a simple multi-level logger. package log import ( - "errors" - "flag" "fmt" + "io" "os" "time" ) @@ -37,100 +36,99 @@ const ( DEBUG ) -// ErrLevel indicates that the specified log level is not valid -var ErrLevel = errors.New("Unknown log level") +// BadLevelError indicates that the specified log level is not valid. +type BadLevelError struct { + level interface{} +} -// SetLevel sets the output level for the global logger -func SetLevel(level int) error { - if level > DEBUG { - return ErrLevel - } - l.level = level - return nil +func (e *BadLevelError) Error() string { return fmt.Sprintf("Unknown log level: %v", e.level) } + +// Logger is a simple multi-level logger. +type Logger struct { + Level int + Writer io.Writer } -// SetLevelString sets the output level for the global logger -func SetLevelString(name string) error { - level, err := StringToLevel(name) - if err != nil || level > DEBUG { - return ErrLevel - } - l.level = level - return nil +func NewLogger(level interface{}, writer io.Writer) *Logger { + var l Logger + l.SetLevel(level) + l.Writer = writer + return &l } -// LevelToString converts a log level to its string representation -func LevelToString(level int) (string, error) { - var s string +// SetLevel sets the output level for the logger. +// 'level' can be either a string or an int. +func (l *Logger) SetLevel(level interface{}) error { switch level { - case ERROR: - s = "ERROR" - case INFO: - s = "INFO" - case DEBUG: - s = "DEBUG" + case ERROR, "ERROR": + l.Level = ERROR + case INFO, "INFO": + l.Level = INFO + case DEBUG, "DEBUG": + l.Level = DEBUG default: - return "", ErrLevel + return &BadLevelError{level} } - return s, nil + return nil } -// StringToLevel converts a string to the corresponding log level -func StringToLevel(name string) (int, error) { - var level int - switch name { - case "ERROR": - level = ERROR - case "INFO": - level = INFO - case "DEBUG": - level = DEBUG - default: - return -1, ErrLevel +// Log logs a message with custom level and type. +// To log messages at predefined levels you can use the convenience +// functions Fatal(), Error(), Info(), Debug(). +func (l *Logger) Log(level int, msgType string, format string, v ...interface{}) { + if level <= l.Level { + msg := fmt.Sprintf(format, v...) + t := time.Now().Format(time.RFC1123) + fmt.Fprintf(l.Writer, "%s [%s]: %s\n", t, msgType, msg) + } - return level, nil } -func FlagLevel(flagname string) *string { - return flag.String(flagname, "ERROR", "set loglevel [ERROR|INFO|DEBUG]") +// Fatal logs a message at "ERROR" level and terminates the program. +func (l *Logger) Fatal(format string, v ...interface{}) { + l.Log(ERROR, "ERROR", format, v...) + os.Exit(1) } -type logger struct { - level int +// Error logs a message at "ERROR" level. +func (l *Logger) Error(format string, v ...interface{}) { + l.Log(ERROR, "ERROR", format, v...) } -var l logger +// Info logs a message at "INFO" level. +func (l *Logger) Info(format string, v ...interface{}) { + l.Log(INFO, "INFO", format, v...) +} -func Log(level int, format string, v ...interface{}) { - if level <= l.level { - msgType, err := LevelToString(level) - if err != nil { - Fatal(err.Error()) - } - msg := fmt.Sprintf(format, v...) - t := time.Now().Format(time.RFC1123) - fmt.Printf("%s [%s]: %s\n", t, msgType, msg) +// Debug logs a message at "DEBUG" level. +func (l *Logger) Debug(format string, v ...interface{}) { + l.Log(DEBUG, "DEBUG", format, v...) +} - } +var DefaultLogger = NewLogger(ERROR, os.Stdout) + +// SetLevel sets the output level for the default logger. +func SetLevel(level interface{}) error { + return DefaultLogger.SetLevel(level) } -// Fatal logs a message at "ERROR" level and terminates the program -func Fatal(format string, v ...interface{}) { - Log(ERROR, format, v...) - os.Exit(1) +// Fatal logs a message at "ERROR" level with the default logger and +// terminates the program. +func Fatal(msg string, v ...interface{}) { + DefaultLogger.Fatal(msg, v...) } -// Error logs a message at "ERROR" level -func Error(format string, v ...interface{}) { - Log(ERROR, format, v...) +// Error logs a message at "ERROR" level with the default logger. +func Error(msg string, v ...interface{}) { + DefaultLogger.Error(msg, v...) } -// Info logs a message at "INFO" level -func Info(format string, v ...interface{}) { - Log(INFO, format, v...) +// Info logs a message at "INFO" level with the default logger. +func Info(msg string, v ...interface{}) { + DefaultLogger.Info(msg, v...) } -// Debug logs a message at "DEBUG" level -func Debug(format string, v ...interface{}) { - Log(DEBUG, format, v...) +// Debug logs a message at "DEBUG" level with the default logger. +func Debug(msg string, v ...interface{}) { + DefaultLogger.Debug(msg, v...) }