// Package ansi can print colored and styled text to your terminal.
package ansi
import (
"fmt"
"log"
)
// Col defines supported colors
type Col int
// defines supported colors
const (
Black = Col(iota)
Red
Green
Yellow
Blue
Magenta
Cyan
White
)
// defines text styling options
const (
TReset = Col(iota)
TBold
TFaint
TItalic
TUnderline
TBlinkSlow
TBlinkFast
TNegative
TConceal
TCrossedOut
)
// Foreground text colors
const (
FgBlack = Col(iota + 30)
FgRed
FgGreen
FgYellow
FgBlue
FgMagenta
FgCyan
FgWhite
)
// Background text colors
const (
BgBlack = Col(iota + 40)
BgRed
BgGreen
BgYellow
BgBlue
BgMagenta
BgCyan
BgWhite
)
// Set sets styling options on strings and stringable interfaces
func Set(str interface{}, Attribute ...Col) string {
var rstr = fmt.Sprint(str)
for _, attr := range Attribute {
rstr = fmt.Sprintf("\033[%vm%v\033[0m", attr, rstr)
}
return rstr
}
// Color adds the color code of col as text color to str and returns as string
func Color(str interface{}, col Col) string {
return fmt.Sprintf("\033[3%vm%v\033[0m", col, str)
}
// BgColor adds the color code of col as background color to str and returns as string
func BgColor(str interface{}, col Col) string {
return fmt.Sprintf("\033[4%vm%v\033[0m", col, str)
}
// Bold surrounds str with the code for bold styled text
func Bold(str interface{}) string {
return fmt.Sprintf("\033[1m%v\033[0m", str)
}
// Underline surrounds str with the code for underlined text
func Underline(str interface{}) string {
return fmt.Sprintf("\033[4m%v\033[0m", str)
}
// Log prints red text via log package
func Log(valuea ...interface{}) interface{} {
if valuea[1] != nil {
log.Println(Color(valuea[1], Red))
}
return valuea[0]
}
// the arg package simplifies cli flags (arguments)
package arg
import (
"bufio"
"flag"
"fmt"
"os"
"time"
)
type argument struct {
name string
defaultValue string
flagValue *string
usage string
timeout time.Duration
argNr int
}
var flagVar = make(map[string]argument)
var values = make(map[string]interface{})
var i = 1
func String(name, defaultValue, usage string, timeout time.Duration) {
flagVar[name] = argument{
name: name,
defaultValue: defaultValue,
flagValue: flag.String(name, "", usage),
usage: usage,
timeout: timeout,
argNr: -1,
}
}
func StringArg(name, defaultValue, usage string, timeout time.Duration) {
flagVar[name] = argument{
name: name,
defaultValue: defaultValue,
flagValue: flag.String(name, "", usage),
usage: usage,
timeout: timeout,
argNr: i,
}
i++
}
func Parse() {
var value interface{}
flag.Parse()
for v := range flagVar {
if *flagVar[v].flagValue != "" {
value = *flagVar[v].flagValue
} else if !(flagVar[v].argNr >= 1 && flagVar[v].argNr < len(os.Args)) && flagVar[v].timeout >= time.Second*1 {
fmt.Printf("# %v: ", flagVar[v].usage)
scanner := bufio.NewScanner(os.Stdin)
ch := make(chan bool, 1)
go func() {
defer func() {
close(ch)
}()
ch <- scanner.Scan()
}()
select {
case <-ch:
value = scanner.Text()
case <-time.After(flagVar[v].timeout):
value = flagVar[v].defaultValue
}
} else if flagVar[v].argNr >= 1 && flagVar[v].argNr < len(os.Args) {
value = os.Args[flagVar[v].argNr]
} else {
value = flagVar[v].defaultValue
}
values[v] = value
}
}
func Get(name string) interface{} {
return values[name]
}
func Dump() string {
return fmt.Sprintf("%v\n", flagVar)
}
// Package as helps by converting various data types to various other types
package as
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
type ree struct {
typ string
re string
}
type Dynamic struct {
Type string
Data interface{}
}
// regex (regular expression) in a slice of ree struct
var regex = [...]ree{
{
typ: "bool",
re: "(0|1|true|false)",
}, {
typ: "int",
re: "\\d+",
}, {
typ: "int",
re: "\\d+[\\^eE]\\d+",
}, {
typ: "float",
re: "[-+]?[0-9]*[\\.,]?[0-9]+([eE][-+]?[0-9]+)?",
}, {
typ: "price",
re: "((€|\\$|¢|£|EURO?|DM|USD) ?)?[-+]?[0-9]*[\\.,]?[0-9]+([eE][-+]?[0-9]+)?( ?(€|\\$|¢|£|EURO?|DM|USD))?",
}, {
typ: "url",
re: "(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?",
}, {
typ: "ipv4",
re: "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])",
}, {
typ: "ipv6",
re: "([0-9A-Fa-f]{1,4}:([0-9A-Fa-f]{1,4}:([0-9A-Fa-f]{1,4}:([0-9A-Fa-f]{1,4}:([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{0,4}|:[0-9A-Fa-f]{1,4})?|(:[0-9A-Fa-f]{1,4}){0,2})|(:[0-9A-Fa-f]{1,4}){0,3})|(:[0-9A-Fa-f]{1,4}){0,4})|:(:[0-9A-Fa-f]{1,4}){0,5})((:[0-9A-Fa-f]{1,4}){2}|:(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\\.(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])){3})|(([0-9A-Fa-f]{1,4}:){1,6}|:):[0-9A-Fa-f]{0,4}|([0-9A-Fa-f]{1,4}:){7}:",
}, {
typ: "mac",
re: "([0-9a-f]{1,2}:){5}([0-9a-f]{1,2})",
}, {
typ: "email",
re: "[a-z0-9\\._%\\+\\-]+\\@[a-z0-9\\.\\-]+\\.[a-z]{2,9}",
}, {
typ: "creditcard",
re: "(?:4\\d{12}(?:\\d{3})?|5[1-5]\\d{14}|3[47]\\d{13}|3(?:0[0-5]|[68]\\d)\\d{11}|6(?:011|5\\d{2})\\d{12}|(?:2131|1800|35\\d{3})\\d{11})",
}, {
typ: "color",
re: "#[a-f0-9]{2,6}",
}, {
typ: "color",
re: "(rgb|hsl|yuv)\\( *[\\d\\.%]+ *, *[\\d\\.%]+ *, *[\\d\\.%]+ *\\)",
}, {
typ: "color",
re: "(rgba|cmyk)\\( *\\d+ *, *\\d+ *, *\\d+ *, *\\d+ *\\)",
}, {
typ: "isbn",
re: "(1(?:(0)|3))?:?[- ]?(\\s)*[0-9]+[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9]*[- ]*[xX0-9]",
}, {
typ: "date",
re: "(?i:([MDCLXVI])((M{0,3})((C[DM])|(D?C{0,3}))?((X[LC])|(L?XX{0,2})|L)?((I[VX])|(V?(II{0,2}))|V)?))",
}, {
typ: "alpha",
re: "[a-zA-Z]+",
}, {
typ: "alphanumeric",
re: "[a-zA-Z0-9]+",
}, {
typ: "base64",
re: "(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})",
}, {
typ: "string",
re: "[[:print:]]+",
}, {
typ: "ascii",
re: "[[:ascii:]]+",
},
}
// timeformats contains the supported time formats
// for the conversion to time.Time.
var timeformats = []string{
time.ANSIC,
time.UnixDate,
time.RubyDate,
time.RFC822,
time.RFC822Z,
time.RFC850,
time.RFC1123,
time.RFC1123Z,
time.RFC3339,
time.RFC3339Nano,
time.Kitchen,
time.Stamp,
time.StampMilli,
time.StampMicro,
time.StampNano,
"Mon, 2 Jan 2006 15:04:05 -0700",
"02.01.06",
"01/02/06",
"2006-01-02",
"2006/01/02",
"01/02/2006",
"02.01.2006",
"01/02/06 15:04",
"2006-01-02 15:04",
"2006-01-02T15:04",
"01/02/2006 15:04",
"02.01.06 15:04:05",
"01/02/06 15:04:05",
"01/02/2006 15:04:05",
"2006-01-02 15:04:05",
"2006-01-02T15:04:05",
"02.01.2006 15:04:05",
"2006-01-02 15:04:05 -0700 MST",
}
// Bool returns a boolean value.
// It mainly depends on the output of strconv.ParseBool,
// but also checks for integer values.
func Bool(valuea ...interface{}) bool {
value := valuea[0]
if Int(value) > 0 {
return true
}
b, _ := strconv.ParseBool(String(value))
return b
}
// Bytes returns a slice of bytes.
func Bytes(valuea ...interface{}) []byte {
value := valuea[0]
if value == nil {
return []byte{}
}
switch val := value.(type) {
case bool:
if val == true {
return []byte("true")
}
return []byte("false")
case string:
return []byte(val)
case []byte:
return val
default:
return []byte(fmt.Sprintf("%v", value))
}
}
// Duration converts input values to time.Duration.
// It mainly depends on time.ParseDuration.
func Duration(valuea ...interface{}) time.Duration {
value := valuea[0]
switch value.(type) {
case int, int8, int16, int32, int64:
return time.Duration(Int(value))
case uint, uint8, uint16, uint32, uint64:
return time.Duration(Int(value))
case float32, float64:
return time.Duration(Int(value))
default:
dur, _ := time.ParseDuration(String(value))
return dur
}
}
// FixedLengthAfter appends spacer chars after a string
func FixedLengthAfter(str string, spacer string, length int) string {
spacer = spacer[:1]
l := length - len(str)
if l > 0 {
return str + strings.Repeat(spacer, l)
}
if l == 0 {
return str
}
return str[:length]
}
// FixedLengthBefore prepends spacer chars before a string
func FixedLengthBefore(str string, spacer string, length int) string {
spacer = spacer[:1]
l := length - len(str)
if l > 0 {
return strings.Repeat(spacer, l) + str
}
if l == 0 {
return str
}
return str[:length]
}
// FixedLengthCenter adds spacer chars after and before a string
func FixedLengthCenter(str string, spacer string, length int) string {
spacer = spacer[:1]
l := length - len(str)
if l > 0 {
if l%2 == 0 {
l = l / 2
return strings.Repeat(spacer, l) + str + strings.Repeat(spacer, l)
}
l = (l + 1) / 2
return strings.Repeat(spacer, l) + str + strings.Repeat(spacer, l-1)
}
if l == 0 {
return str
}
return str[:length]
}
// Float converts it's input to type float64.
// int, uint and float gets converted as expected,
// time is transformed to a float of the corresponding timestamp.
// strings and byte slices gets converted via strconv.ParseFloat.
func Float(valuea ...interface{}) float64 {
value := valuea[0]
switch val := value.(type) {
case int:
return float64(val)
case int8:
return float64(val)
case int16:
return float64(val)
case int32:
return float64(val)
case int64:
return float64(val)
case uint:
return float64(val)
case uint8:
return float64(val)
case uint16:
return float64(val)
case uint32:
return float64(val)
case uint64:
return float64(val)
case float32:
return float64(val)
case float64:
return float64(val)
case time.Time:
return float64(val.Unix())
case bool:
if val == true {
return float64(1)
}
return float64(0)
default:
f, _ := strconv.ParseFloat(String(value), 64)
return float64(f)
}
}
// FloatFromXString converts strings to float64.
// Most values can be converted to float via Float(),
// but floats as strings in e.g. german spelling
// should be converted with this function.
func FloatFromXString(valuea ...string) float64 {
value := valuea[0]
value = strings.Trim(value, "\t\n\r¢§$€ ")
var float float64
c := strings.Count(value, ",")
p := strings.Count(value, ".")
fc := strings.Index(value, ",")
fp := strings.Index(value, ".")
if c == 0 && p == 1 {
float, _ = strconv.ParseFloat(value, 64)
} else if c == 1 && p == 0 {
value = strings.Replace(value, ",", ".", 1)
float, _ = strconv.ParseFloat(value, 64)
} else if c == 0 && p == 0 {
intx, _ := strconv.ParseInt(value, 0, 64)
float = float64(intx)
} else if c > 1 && p < 2 {
value = strings.Replace(value, ",", "", -1)
float, _ = strconv.ParseFloat(value, 64)
} else if c < 2 && p > 1 {
value = strings.Replace(value, ".", "", -1)
value = strings.Replace(value, ",", ".", 1)
float, _ = strconv.ParseFloat(value, 64)
} else if c == 1 && p == 1 {
if fp < fc {
value = strings.Replace(value, ".", "", -1)
value = strings.Replace(value, ",", ".", 1)
} else {
value = strings.Replace(value, ",", "", -1)
}
float, _ = strconv.ParseFloat(value, 64)
} else {
value = "0"
float, _ = strconv.ParseFloat(value, 64)
}
return float64(float)
}
// Int returns an int64 of the input value.
// Float values and float values in strings will be rounded via
// "round half towards positive infinity".
// strings get converted via strconv.ParseFloat.
func Int(valuea ...interface{}) int64 {
value := valuea[0]
switch val := value.(type) {
case int:
return int64(val)
case int8:
return int64(val)
case int16:
return int64(val)
case int32:
return int64(val)
case int64:
return int64(val)
case uint:
return int64(val)
case uint8:
return int64(val)
case uint16:
return int64(val)
case uint32:
return int64(val)
case uint64:
return int64(val)
case float32:
return int64(val + 0.5)
case float64:
return int64(val + 0.5)
case time.Time:
return int64(val.Unix())
case bool:
if val == true {
return int64(1)
}
return int64(0)
default:
i, _ := strconv.ParseFloat(String(value), 64)
return int64(i + 0.5)
}
}
// String converts input values to string.
// Time and Duration gets converted via standard functions.
// Most types gets "converted" via fmt.Sprintf.
func String(valuea ...interface{}) string {
value := valuea[0]
if value == nil {
return ""
}
switch val := value.(type) {
case bool:
if value.(bool) == true {
return "true"
}
return "false"
case time.Duration:
return string(val.String())
case time.Time:
return string(val.Format(time.RFC3339))
case string:
return val
case []byte:
return string(val)
default:
return fmt.Sprintf("%v", val)
}
}
// Time converts inputs values to time.Time.
// Time formats in the variable timeformats can be used.
func Time(valuea ...interface{}) time.Time {
value := valuea[0]
s := String(value)
for _, format := range timeformats {
r, err := time.Parse(format, s)
if err == nil {
return r
}
}
return time.Time{}
}
// Trimmed takes the first given value, converts it to
// a string, trims the whitespace an returns it.
func Trimmed(valuea ...interface{}) string {
value := valuea[0]
return strings.TrimSpace(String(value))
}
// Uint returns an uint64 of the input value.
// Float values and float values in strings will be rounded via
// "round half towards positive infinity".
// strings get converted via strconv.ParseFloat.
func Uint(valuea ...interface{}) uint64 {
value := valuea[0]
switch val := value.(type) {
case int:
return uint64(val)
case int8:
return uint64(val)
case int16:
return uint64(val)
case int32:
return uint64(val)
case int64:
return uint64(val)
case uint:
return uint64(val)
case uint8:
return uint64(val)
case uint16:
return uint64(val)
case uint32:
return uint64(val)
case uint64:
return uint64(val)
case float32:
return uint64(val + 0.5)
case float64:
return uint64(val + 0.5)
case time.Time:
return uint64(val.Unix())
case bool:
if val == true {
return uint64(1)
}
return uint64(0)
default:
i, _ := strconv.ParseFloat(String(value), 64)
return uint64(i + 0.5)
}
}
// Type returns a type (string) of a string.
func Type(valuea ...interface{}) (string, error) {
var err error
str := strings.Trim(String(valuea[0]), " \t\n\r")
if !Time(str).IsZero() {
return "date", nil
}
for _, b := range regex {
var match bool
re := "(?i)^" + b.re + "$"
if match, err = regexp.MatchString(re, str); match == true {
//fmt.Printf("%v tested for %v with %v; result: %v\n", str, b.typ, b.re, match)
return b.typ, nil
}
//fmt.Printf("%v tested for %v with %v; result: %v\n", str, b.typ, b.re, match)
}
return "", err
}
// DBType returns a Database Type of a string.
func DBType(str string) string {
t, err := Type(str)
if err != nil {
return "string"
}
switch t {
case "bool", "int", "string", "float":
return t
default:
return "string"
}
}
// DBTypeMultiple returns the lowest common denominator of a type for all inserted DBTypes
func DBTypeMultiple(val []string) string {
var typeint int
for _, typ := range val {
for i, b := range regex {
if b.typ == typ {
if typeint < i {
typeint = i
}
}
}
}
return regex[typeint].typ
}
package as
import (
"strconv"
)
type Int64 int64
// MarshalJSON implements json.Marshaler interface for the Int64 Type.
func (i Int64) MarshalJSON() ([]byte, error) {
return Bytes(i), nil
}
// UnmarshalJSON implements json.Unmarshaler inferface for the Int64 Type.
func (i *Int64) UnmarshalJSON(buf []byte) error {
var i64 Int64
str, err := strconv.Unquote(string(buf))
if err == nil {
i64 = Int64(Int(str))
} else {
i64 = Int64(Int(buf))
}
*i = i64
return nil
}
// UnmarshalJSON implements json.Unmarshaler inferface for the Dynamic Type.
func (d *Dynamic) UnmarshalJSON(buf []byte) error {
var dyn Dynamic
str, err := strconv.Unquote(string(buf))
if err != nil {
str = string(buf)
}
dyn.Type, _ = Type(str)
switch dyn.Type {
case "date":
dyn.Data = Time(str)
default:
dyn.Data = str
}
*d = dyn
return nil
}
package bitmask
import "fmt"
type Bitmask struct {
value int
}
func New(init int) *Bitmask {
return &Bitmask{value: init}
}
func (b *Bitmask) setBit(pos int) int {
b.value |= (1 << pos)
return b.value
}
func (b *Bitmask) clearBit(pos int) int {
mask := ^(1 << pos)
b.value &= mask
return b.value
}
func (b *Bitmask) Set(pos int, val bool) int {
if val == true {
return b.setBit(pos)
}
return b.clearBit(pos)
}
func (b *Bitmask) hasBit(pos int) bool {
return ((b.value & (1 << pos)) > 0)
}
func (b *Bitmask) Get(pos int) bool {
return b.hasBit(pos)
}
func (b *Bitmask) Int() int {
return b.value
}
func (b *Bitmask) String() string {
return fmt.Sprintf("%b", b.value)
}
func (b *Bitmask) Byte() []byte {
return []byte{byte(b.value)}
}
// Package cache simplifies caching with GC
package cache
import (
"encoding/gob"
"fmt"
"io"
"os"
"os/signal"
"sort"
"sync"
"syscall"
"time"
)
// Item as a single object with an interface as value and two time.Time values for creation and expiration dates
type Item struct {
Object interface{}
Creation time.Time
Expiration time.Time
}
func (item *Item) isExpired() bool {
if item.Expiration.IsZero() {
return false
}
return item.Expiration.Before(time.Now())
}
// Cache is a storage for all Item items
type Cache struct {
Expiration time.Duration
items map[string]*Item
lock sync.RWMutex
}
// Export all items to a gob buffer
func (cache *Cache) Export(w io.Writer) error {
enc := gob.NewEncoder(w)
cache.lock.RLock()
defer cache.lock.RUnlock()
X := make(map[string]interface{})
for k := range cache.items {
X[k] = cache.items[k].Object
}
if err := enc.Encode(X); err != nil {
return err
}
return nil
}
// Import all items from a gob buffer
func (cache *Cache) Import(r io.Reader) error {
dec := gob.NewDecoder(r)
X := make(map[string]interface{})
if err := dec.Decode(&X); err != nil {
return err
}
for k, v := range X {
cache.Set(k, v)
}
return nil
}
// String returns all cached values as string (for debugging)
func (cache *Cache) String() string {
var str string
var keys []string
cache.lock.RLock()
defer cache.lock.RUnlock()
for k := range cache.items {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
str += k + "\t" + fmt.Sprintf("%v", cache.items[k].Object) + "\n"
}
return str
}
// Set creates an Item in the cache, if there is already an item with that name it get overwritten
func (cache *Cache) Set(key string, value interface{}) {
cache.lock.Lock()
defer cache.lock.Unlock()
cache.items[key] = &Item{
Object: value,
Creation: time.Now(),
Expiration: time.Now().Add(cache.Expiration),
}
}
// SetWithDuration does the same as Set but with an specific expiration date
func (cache *Cache) SetWithDuration(key string, value interface{}, creation time.Time, duration time.Duration) {
cache.lock.Lock()
defer cache.lock.Unlock()
cache.items[key] = &Item{
Object: value,
Creation: creation,
Expiration: time.Now().Add(duration),
}
}
// Time returns the creation date of a cached item
func (cache *Cache) Time(key string) time.Time {
return cache.items[key].Creation
}
// Get returns the value of a cached item or nil if expired
func (cache *Cache) Get(key string) interface{} {
cache.lock.RLock()
defer cache.lock.RUnlock()
item, ok := cache.items[key]
if !ok || item.isExpired() {
return nil
}
return item.Object
}
// Delete deletes a cached item
func (cache *Cache) Delete(key string) {
cache.lock.Lock()
defer cache.lock.Unlock()
delete(cache.items, key)
}
// Add creates an cached item
func (cache *Cache) Add(key string, value interface{}) bool {
item := cache.Get(key)
if item != nil {
return false
}
cache.Set(key, value)
return true
}
// Update changes the value of an key. If the key doesn't exist, it returns false
func (cache *Cache) Update(key string, value interface{}) bool {
item := cache.Get(key)
if item == nil {
return false
}
cache.Set(key, value)
return true
}
// DeleteExpired checks all cache items and deletes the expired items
func (cache *Cache) DeleteExpired() {
cache.lock.Lock()
defer cache.lock.Unlock()
for k, v := range cache.items {
if v.isExpired() {
delete(cache.items, k)
}
}
}
// DeleteExpiredWithFunc does the same like DeleteExpired
// but also calls a function for each deleted item
func (cache *Cache) DeleteExpiredWithFunc(fn func(key string, value interface{})) {
cache.lock.Lock()
defer cache.lock.Unlock()
for k, v := range cache.items {
if v.isExpired() {
fn(k, cache.items[k].Object)
delete(cache.items, k)
}
}
}
// DeleteAllWithFunc does the same like DeleteExpiredWithFunc
// but not just for the expired items, also the non expired
func (cache *Cache) DeleteAllWithFunc(fn func(key string, value interface{})) {
cache.lock.Lock()
defer cache.lock.Unlock()
for k := range cache.items {
fn(k, cache.items[k].Object)
delete(cache.items, k)
}
}
// Size returns the number of cached items
// it does not check for expired items, so run DeleteExpired before
func (cache *Cache) Size() int {
n := len(cache.items)
return n
}
// Clear removes all items in the cache
func (cache *Cache) Clear() {
cache.lock.Lock()
defer cache.lock.Unlock()
cache.items = map[string]*Item{}
}
func cleaner(cache *Cache, interval time.Duration) {
ticker := time.Tick(interval)
for {
select {
case <-ticker:
cache.DeleteExpired()
}
}
}
func cleanerWithFunc(cache *Cache, interval time.Duration, fn func(key string, value interface{})) {
defer cache.DeleteAllWithFunc(fn)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
ticker := time.Tick(interval)
for {
select {
case <-ticker:
cache.DeleteExpiredWithFunc(fn)
case <-c:
cache.DeleteAllWithFunc(fn)
os.Exit(1)
}
}
}
// New creates a new Cache
func New(expirationTime, cleanupInterval time.Duration) *Cache {
items := make(map[string]*Item)
if expirationTime == 0 {
expirationTime = -1
}
cache := &Cache{
Expiration: expirationTime,
items: items,
}
go cleaner(cache, cleanupInterval)
return cache
}
// New2 creates a new Cache with a cleaner function
func New2(expirationTime, cleanupInterval time.Duration, fn func(key string, value interface{})) *Cache {
items := make(map[string]*Item)
if expirationTime == 0 {
expirationTime = -1
}
cache := &Cache{
Expiration: expirationTime,
items: items,
}
go cleanerWithFunc(cache, cleanupInterval, fn)
return cache
}
// simplifies file access and adds a simple caching method
package cachedfile
import (
"fmt"
"simonwaldherr.de/go/golibs/cache"
"simonwaldherr.de/go/golibs/file"
"time"
)
var fileCache *cache.Cache
var cacheInit bool
func cacheWorker(filename string, value interface{}) {
_, mtime, _, err := file.Time(filename)
modify := mtime.UnixNano()
if err == nil && modify < fileCache.Time(filename).UnixNano() {
file.Write(filename, fmt.Sprint(value), false)
}
}
func Init(expirationTime, cleanupInterval time.Duration) {
if cacheInit == true {
fileCache.DeleteAllWithFunc(cacheWorker)
}
cacheInit = true
fileCache = cache.New2(expirationTime, cleanupInterval, cacheWorker)
}
func Read(filename string) (string, error) {
if cacheInit == false {
cacheInit = true
fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
}
var err error
var data string
filename, err = file.GetAbsolutePath(filename)
if err != nil {
return "", err
}
if xdata := fileCache.Get(filename); xdata == nil {
if data, err = file.Read(filename); err != nil {
return "", err
}
_, mtime, _, err := file.Time(filename)
if err != nil {
mtime = time.Now()
}
duration, _ := time.ParseDuration("2h30m")
fileCache.SetWithDuration(filename, data, mtime, duration)
} else {
data = fmt.Sprint(xdata)
}
return data, nil
}
func Write(filename, str string, append bool) error {
if cacheInit == false {
cacheInit = true
fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
}
var err error
var data string
filename, err = file.GetAbsolutePath(filename)
if err != nil {
return err
}
if append {
data, err = Read(filename)
if err != nil {
return err
}
fileCache.Set(filename, data+str)
} else {
fileCache.Set(filename, str)
}
return nil
}
func Size(filename string) (int64, error) {
str, err := Read(filename)
if err != nil {
return -1, err
}
return int64(len(str)), nil
}
func Clean(filename string) error {
return Write(filename, "", false)
}
func Stop() {
if cacheInit == true {
fileCache.DeleteAllWithFunc(cacheWorker)
}
}
func Reset() {
fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
cacheInit = false
}
package channel
type Communication struct {
receiver map[chan interface{}]bool
addReceiver chan chan interface{}
rmReceiver chan chan interface{}
messages chan interface{}
}
func Init() *Communication {
var hub = &Communication{
receiver: make(map[chan interface{}]bool),
addReceiver: make(chan (chan interface{})),
rmReceiver: make(chan (chan interface{})),
messages: make(chan interface{}, 31),
}
go func() {
for {
select {
case s := <-hub.addReceiver:
hub.receiver[s] = true
case s := <-hub.rmReceiver:
delete(hub.receiver, s)
if len(hub.receiver) == 0 {
return
}
case msg := <-hub.messages:
for rec := range hub.receiver {
go func(message interface{}, receiver chan interface{}) {
receiver <- message
}(msg, rec)
}
}
}
}()
return hub
}
func (hub *Communication) AddReceiver() chan interface{} {
messageChannel := make(chan interface{})
hub.addReceiver <- messageChannel
return messageChannel
}
func (hub *Communication) CloseReceiver(ch chan interface{}) int {
hub.rmReceiver <- ch
return hub.CountReceiver()
}
func (hub *Communication) CountReceiver() int {
return len(hub.receiver)
}
func (hub *Communication) AddTransmitter() chan<- interface{} {
return hub.messages
}
package csv
import (
"bufio"
"encoding/csv"
"io"
"os"
"strings"
)
// LoadCSVfromFile parses a CSV from a file and returns two maps
// one map with the data and another one with the csv head
func LoadCSVfromFile(filename string) (map[int][]string, map[string]int) {
fp, _ := os.Open(filename)
return loadCSV(bufio.NewReader(fp))
}
// LoadCSVfromString parses a CSV from a string and returns two maps
// one map with the data and another one with the csv head
func LoadCSVfromString(csv string) (map[int][]string, map[string]int) {
fp := strings.NewReader(csv)
return loadCSV(fp)
}
func loadCSV(reader io.Reader) (map[int][]string, map[string]int) {
var row int
var head = map[int][]string{}
var data = map[int][]string{}
csvReader := csv.NewReader(reader)
csvReader.Comma = ';'
for {
record, err := csvReader.Read()
if err == io.EOF {
break
}
if row == 0 {
head[row] = record
} else {
data[row] = record
}
row++
}
return data, GetHead(head)
}
// GetHead returns a map which represents the head of a csv map
func GetHead(data map[int][]string) map[string]int {
head := make(map[string]int, len(data[0]))
for pos, name := range data[0] {
head[name] = pos
}
return head
}
// file wraps around the standard functions to simplify reading and writing on disk
package file
import (
"bufio"
"bytes"
"io"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"simonwaldherr.de/go/golibs/gopath"
"strings"
)
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
func Exists(fn string) bool {
if _, err := os.Stat(fn); err == nil {
return true
}
return false
}
func IsDir(fn string) bool {
file, err := os.Stat(fn)
if err != nil {
return false
}
if file.IsDir() {
return true
}
return false
}
func IsFile(fn string) bool {
file, err := os.Stat(fn)
if err != nil {
return false
}
if file.Mode().IsRegular() {
return true
}
return false
}
func isSymlink(fi os.FileInfo) bool {
return fi.Mode()&os.ModeSymlink == os.ModeSymlink
}
func IsSymlink(fn string) bool {
file, err := os.Lstat(fn)
if err != nil {
return false
}
return isSymlink(file)
}
func Read(fn string) (string, error) {
var file *os.File
var err error
buf := bytes.NewBuffer(nil)
file, err = os.Open(fn)
if err != nil {
return "", err
}
defer file.Close()
if _, err = io.Copy(buf, file); err != nil {
return "", err
}
s := string(buf.Bytes())
return s, nil
}
func ReadUntil(fn string, delim []string) (string, string, int, error) {
file, err := os.Open(fn)
defer file.Close()
if err != nil {
return "", "", 0, err
}
reader := bufio.NewReader(file)
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanRunes)
pos := 0
buf := ""
for scanner.Scan() {
char := scanner.Text()
if contains(delim, char) {
return buf, char, pos, nil
}
buf += char
pos++
}
return buf, "", pos, nil
}
func ReadBlocks(fn string, delim []string, fnc func(string) (string, error)) (string, error) {
file, err := os.Open(fn)
defer file.Close()
if err != nil {
return "", err
}
reader := bufio.NewReader(file)
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanRunes)
pos := 0
buf := ""
ret := ""
str := ""
for scanner.Scan() {
char := scanner.Text()
if contains(delim, char) {
str, err = fnc(buf)
ret += str
buf = ""
}
buf += char
pos++
}
return ret, nil
}
func Write(fn, str string, append bool) error {
var file *os.File
var err error
if append {
file, err = os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.FileMode(0666))
} else {
file, err = os.OpenFile(fn, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(0666))
}
if err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(str); err != nil {
return err
}
if err = file.Sync(); err != nil {
return err
}
return nil
}
func Size(fn string) (int64, error) {
file, err := os.Open(fn)
defer file.Close()
if err != nil {
return -1, err
}
fi, err := file.Stat()
if err != nil {
return -1, err
}
return fi.Size(), nil
}
func Clean(fn string) error {
return Write(fn, "", false)
}
func Rename(from, to string) error {
err := os.Rename(from, to)
if err != nil {
return err
}
return nil
}
func Copy(from, to string) error {
r, err := os.Open(from)
if err != nil {
return err
}
defer r.Close()
w, err := os.Create(to)
if err != nil {
return err
}
defer w.Close()
_, err = io.Copy(w, r)
if err != nil {
return err
}
return nil
}
func Delete(fn string) error {
err := os.Remove(fn)
if err != nil {
return err
}
return nil
}
func ReadDir(dn string) ([]string, error) {
var flist []string
dn, err := GetAbsolutePath(dn)
if err != nil {
return []string{""}, err
}
files, err := ioutil.ReadDir(dn)
if err != nil {
return []string{""}, err
}
for _, f := range files {
flist = append(flist, f.Name())
}
return flist, nil
}
func Each(dirname string, recursive bool, fnc func(string, string, string, bool, os.FileInfo)) error {
file, err := os.Open(dirname)
defer file.Close()
if err != nil {
return err
}
list, err := file.Readdir(-1)
if err != nil {
return err
}
for _, fi := range list {
path, _ := filepath.Abs(dirname + string(os.PathSeparator) + fi.Name())
isDir := fi.IsDir()
split := strings.Split(fi.Name(), ".")
suffix := split[len(split)-1:][0] // if you want the real filetype, use http.DetectContentType
fnc(fi.Name(), suffix, path, isDir, fi)
if recursive && isDir {
err = Each(path, recursive, fnc)
if err != nil {
return err
}
}
}
return nil
}
var HomeDir string
func SetHomeDir() string {
if HomeDir == "#" || runtime.GOOS == "windows" {
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
if home == "" {
home = os.Getenv("USERPROFILE")
}
HomeDir = home
return home
}
HomeDir = os.Getenv("HOME")
return os.Getenv("HOME")
}
func FakeHomeDir(dir string) string {
HomeDir = dir
return dir
}
func GetHomeDir() string {
if HomeDir == "" {
return SetHomeDir()
}
return HomeDir
}
func switchSymlink(path []byte, start int, link, after string) []byte {
if link[0] == os.PathSeparator {
return []byte(filepath.Join(link, after))
}
return []byte(filepath.Join(string(path[0:start]), link, after))
}
func nextComponent(path []byte, start int) []byte {
v := bytes.IndexByte(path[start:], os.PathSeparator)
if v < 0 {
return path
}
return path[0 : start+v]
}
// GetAbsolutePath returns the absolute path to a file or dir
// if it is a relative path it is relative to the current working directory
func GetAbsolutePath(fn string) (string, error) {
pwd, err := os.Getwd()
if err != nil {
return "", err
}
return getAbsolutePathHelper(fn, pwd)
}
// GetAbsolutePathByApp returns the absolute path to a file or dir
// if it is a relative path it is relative to the Go Application source or binary
func GetAbsolutePathByApp(fn string) (string, error) {
pwd := gopath.Dir()
return getAbsolutePathHelper(fn, pwd)
}
func getAbsolutePathHelper(fn string, pwd string) (string, error) {
if len(fn) == 0 {
return "", os.ErrInvalid
}
if fn[0] != os.PathSeparator {
if fn[0] == '.' {
fn = filepath.Join(pwd, fn)
} else {
fn = strings.Replace(fn, "~", GetHomeDir(), 1)
}
}
path := []byte(fn)
nlinks := 0
start := 1
prev := 1
for start < len(path) {
c := nextComponent(path, start)
cur := c[start:]
if len(cur) == 0 {
copy(path[start:], path[start+1:])
path = path[0 : len(path)-1]
} else if len(cur) == 1 && cur[0] == '.' {
if start+2 < len(path) {
copy(path[start:], path[start+2:])
}
path = path[0 : len(path)-2]
} else if len(cur) == 2 && cur[0] == '.' && cur[1] == '.' {
copy(path[prev:], path[start+2:])
path = path[0 : len(path)+prev-(start+2)]
prev = 1
start = 1
} else {
fi, err := os.Lstat(string(c))
if err == nil {
if isSymlink(fi) {
nlinks++
if nlinks > 8 {
return "", os.ErrInvalid
}
var link string
link, err = os.Readlink(string(c))
after := string(path[len(c):])
path = switchSymlink(path, start, link, after)
prev = 1
start = 1
} else {
prev = start
start = len(c) + 1
}
} else {
return string(path), nil
}
}
}
for len(path) > 1 && path[len(path)-1] == os.PathSeparator {
path = path[0 : len(path)-1]
}
return string(path), nil
}
package file
import (
"os"
"syscall"
"time"
)
func Time(fn string) (time.Time, time.Time, time.Time, error) {
file, err := os.Stat(fn)
if err != nil {
return time.Time{}, time.Time{}, time.Time{}, err
}
mtime := file.ModTime()
stat := file.Sys().(*syscall.Stat_t)
atime := time.Unix(int64(stat.Atimespec.Sec), int64(stat.Atimespec.Nsec))
ctime := time.Unix(int64(stat.Ctimespec.Sec), int64(stat.Ctimespec.Nsec))
return atime, mtime, ctime, nil
}
// run a function for each ...
package foreach
import (
"encoding/json"
"os"
"reflect"
"simonwaldherr.de/go/golibs/file"
"simonwaldherr.de/go/golibs/node"
"simonwaldherr.de/go/golibs/structs"
//"encoding/xml"
)
func File(dirname string, recursive bool, fnc func(string, string, string, bool, os.FileInfo)) error {
return file.Each(dirname, recursive, fnc)
}
func JSON(str string, handler func(*string, *int, *interface{}, int)) error {
var j interface{}
err := json.Unmarshal([]byte(str), &j)
if err == nil {
node.Node(&j, handler)
return nil
}
return err
}
func Struct(sstruct interface{}, handler func(string, string, interface{}, int)) {
v := reflect.ValueOf(sstruct)
t := reflect.TypeOf(sstruct)
structs.ReflectHelper(v, t, 0, handler)
}
/*
func XML(str string, handler func(*string, *int, *interface{}, int)) error {
var x interface{}
err := xml.Unmarshal([]byte(str), &x)
if err == nil {
Node(&x, handler)
return nil
}
return err
}*/
// gopath provides an easy way to get system information
package gopath
import (
"os"
"path"
"path/filepath"
"runtime"
"strings"
"unicode/utf8"
)
type FileType int
const (
DontExist FileType = iota
Directory
ASCII
Binary
)
// GetFileType returns the type of a given file.
// The return values can be Directory, ASCII, Binary
// or DontExist.
func GetFileType(name string) FileType {
if file, err := os.Open(name); err == nil {
if fileStat, err := file.Stat(); err == nil && fileStat.IsDir() {
return Directory
} else if err == nil {
data := make([]byte, 100)
file.Read(data)
if utf8.Valid(data) {
return ASCII
}
return Binary
}
}
return DontExist
}
// Compiler returns the name of the used compiler
func Compiler() string {
return runtime.Compiler
}
// GOARCH returns the system architecture
func GOARCH() string {
return runtime.GOARCH
}
// GOOS returns the name of the Operating System
func GOOS() string {
return runtime.GOOS
}
// GOROOT returns the $GOROOT environment variable
func GOROOT() string {
return runtime.GOROOT()
}
// GOPATH returns the $GOPATH environment variable
func GOPATH() string {
return os.Getenv("GOPATH")
}
// WD returns the working directory, this is
// the folder where you were last.
func WD() string {
dir, _ := os.Getwd()
return dir
}
// Compiled returns a boolean which tells if you run
// a already compiled binary or "go run" the source.
func Compiled() bool {
if strings.HasPrefix(os.Args[0], "/var/folders/") ||
strings.HasPrefix(os.Args[0], "/tmp/go-build") ||
strings.Contains(os.Args[0], "\\AppData\\Local\\Temp\\") {
return false
}
return true
}
// gpath is the helper function for the public functions
// Path(), Name() and Dir()
func gpath(i int) string {
var filename string
if Compiled() == false {
_, filename, _, _ = runtime.Caller(i)
} else {
filename, _ = filepath.Abs(filepath.Join(WD(), os.Args[0]))
}
return filename
}
// Path returns the full path of the binary or source file
// which is currently running.
func Path() string {
return gpath(2)
}
// Name returns the filename of the binary or source file
// which is currently running.
func Name() string {
_, f := path.Split(gpath(2))
return f
}
// Dir returns the directory in which the binary or source file
// is stored. This is useful if you have config files in the
// same folder.
func Dir() string {
return filepath.Dir(gpath(2))
}
// manipulating images pixel by pixel
package graphics
import (
"image"
"image/color"
"image/draw"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"math"
"os"
)
var edfX = [3][3]int8{{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}}
var edfY = [3][3]int8{{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}}
func EachPixel(file *os.File, f func(uint8, uint8, uint8, uint8) (uint8, uint8, uint8, uint8)) (image.Image, error) {
src, _, err := image.Decode(file)
if err != nil {
return nil, err
}
bsrc := src.Bounds()
img := image.NewRGBA(bsrc)
draw.Draw(img, bsrc, src, bsrc.Min, draw.Src)
b := img.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
oldPixel := img.At(x, y)
r1, g1, b1, a1 := oldPixel.RGBA()
r2, g2, b2, a2 := f(uint8(r1), uint8(g1), uint8(b1), uint8(a1))
pixel := color.RGBA{uint8(r2), uint8(g2), uint8(b2), uint8(a2)}
img.Set(x, y, pixel)
}
}
return img, nil
}
func ResizeNearestNeighbor(file *os.File, newWidth, newHeight int) (*image.NRGBA, error) {
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return NearestNeighbor(img, newWidth, newHeight)
}
func NearestNeighbor(img image.Image, newWidth, newHeight int) (*image.NRGBA, error) {
w := img.Bounds().Max.X
h := img.Bounds().Max.Y
newimg := image.NewNRGBA(image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{newWidth, newHeight}})
xn := (w<<16)/newWidth + 1
yn := (h<<16)/newHeight + 1
for yo := 0; yo < newHeight; yo++ {
y := (yo * yn) >> 16
for xo := 0; xo < newWidth; xo++ {
x := (xo * xn) >> 16
newimg.Set(xo, yo, img.At(x, y))
}
}
return newimg, nil
}
func Grayscale(img image.Image) image.Image {
min := img.Bounds().Min
max := img.Bounds().Max
grayImg := image.NewGray(image.Rect(max.X, max.Y, min.X, min.Y))
for x := 0; x < max.X; x++ {
for y := 0; y < max.Y; y++ {
grayPixel := color.GrayModel.Convert(img.At(x, y))
grayImg.Set(x, y, grayPixel)
}
}
return grayImg
}
func Edgedetect(img image.Image) image.Image {
img = Grayscale(img)
max := img.Bounds().Max
min := img.Bounds().Min
edImg := image.NewGray(image.Rect(max.X, max.Y, min.X, min.Y))
width := max.X
height := max.Y
var pixel color.Color
for x := 1; x < width-1; x++ {
for y := 1; y < height-1; y++ {
pX, pY := detectEdgeAroundPixel(img, x, y)
val := uint32(math.Ceil(math.Sqrt(pX*pX + pY*pY)))
pixel = color.Gray{Y: uint8(val)}
edImg.SetGray(x, y, pixel.(color.Gray))
}
}
return edImg
}
func detectEdgeAroundPixel(img image.Image, x int, y int) (float64, float64) {
var pX, pY int
curX := x - 1
curY := y - 1
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
r, _, _, _ := img.At(curX, curY).RGBA()
pX += int(edfX[i][j]) * int(uint8(r))
pY += int(edfY[i][j]) * int(uint8(r))
curX++
}
curX = x - 1
curY++
}
return math.Abs(float64(pX)), math.Abs(float64(pY))
}
package http
import (
//"context"
"io/ioutil"
//"net"
"net/http"
//"strings"
"time"
//"github.com/rs/dnscache"
)
var Transporter *http.Transport
var client http.Client
var clientReady bool = false
func init() {
//dnsResolver := &dnscache.Resolver{}
Transporter = &http.Transport{
/*
DialContext: func(ctx context.Context, network string, addr string) (conn net.Conn, err error) {
separator := strings.LastIndex(addr, ":")
ips, err := dnsResolver.LookupHost(ctx, addr[:separator])
if err != nil {
return nil, err
}
for _, ip := range ips {
conn, err = net.Dial(network, ip+addr[separator:])
if err == nil {
break
}
}
return
},*/
MaxIdleConns: 1024,
MaxConnsPerHost: 1024,
IdleConnTimeout: 10 * time.Second,
}
/*
go func() {
cacheTicker := time.NewTicker(30 * time.Minute)
defer cacheTicker.Stop()
for range cacheTicker.C {
dnsResolver.Refresh(true)
}
}()*/
}
var NewRequest = http.NewRequest
func Client(timeout time.Duration) http.Client {
return http.Client{
Transport: Transporter,
Timeout: timeout,
}
}
func GetString(url string) (string, error) {
if clientReady == false {
client = Client(time.Second * 15)
}
resp, err := client.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
package log
import (
"io"
"log"
"os"
)
var (
Info *log.Logger
Warning *log.Logger
Error *log.Logger
Fatal *log.Logger
)
func init() {
Info = log.New(os.Stdout, "INFO: ", log.Ltime|log.Lshortfile)
Warning = log.New(os.Stderr, "WARNING: ", log.Ltime|log.Lshortfile)
Error = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
Fatal = log.New(os.Stderr, "FATAL: ", log.Ldate|log.Lmicroseconds|log.Llongfile)
}
func Change(infoHandle, warningHandle, errorHandle, fatalHandle io.Writer,
infoFlag, warningFlag, errorFlag, fatalFlat int) {
Info = log.New(infoHandle,
"INFO: ", infoFlag)
Warning = log.New(warningHandle,
"WARNING: ", warningFlag)
Error = log.New(errorHandle,
"ERROR: ", errorFlag)
Fatal = log.New(fatalHandle,
"FATAL: ", fatalFlat)
}
// wanna do something several times?
// Then this is the correct package for you.
package re
import (
"fmt"
"time"
)
var MaxAttempts = 10
func Try(retrys int, fn func() (err error)) error {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
var err error
for attempt := 1; ; attempt++ {
err = fn()
if err == nil {
break
}
if attempt >= MaxAttempts || attempt >= retrys {
return fmt.Errorf("Reached number of attempts (%v)\n%v", (attempt), err)
}
}
return err
}
func Do(wait time.Duration, fn func(chan<- interface{})) (<-chan interface{}, chan<- bool) {
stop := make(chan bool, 1)
ret := make(chan interface{})
go func() {
defer func() {
close(stop)
time.Sleep(wait)
close(ret)
}()
var stopbool = false
for stopbool == false {
select {
case <-stop:
stopbool = true
default:
go fn(ret)
time.Sleep(wait)
}
}
}()
return ret, stop
}
// regex is a wrapper for the standard regexp package.
// It automates the regexp.Compile process for you.
package regex
import (
"regexp"
)
var regexArray = make(map[string]*regexp.Regexp)
func CheckRegex(regex string) error {
_, err := regexp.Compile(regex)
return err
}
func CacheRegex(regex string) error {
var err error
if _, ok := regexArray[regex]; ok {
return nil
}
if re, err := regexp.Compile(regex); err == nil {
regexArray[regex] = re
}
return err
}
func MatchString(src, regex string) (bool, error) {
var err error
if re, ok := regexArray[regex]; ok {
return re.MatchString(src), nil
}
if re, err := regexp.Compile(regex); err == nil {
regexArray[regex] = re
return re.MatchString(src), nil
}
return false, err
}
// ReplaceAllString returns a copy of src, replacing matches of the regular expression
// with the replacement string replace. Inside replace, $ signs are interpreted as
// in Expand, so for instance $1 represents the text of the first submatch.
func ReplaceAllString(src, regex, replace string) (string, error) {
var err error
if re, ok := regexArray[regex]; ok {
return re.ReplaceAllString(src, replace), nil
}
if re, err := regexp.Compile(regex); err == nil {
regexArray[regex] = re
return re.ReplaceAllString(src, replace), nil
}
return "", err
}
// ReplaceAllStringFunc returns a copy of src in which all matches of the
// regular expression have been replaced by the return value of function replace applied
// to the matched substring. The replacement returned by replace is substituted
// directly, without using Expand.
func ReplaceAllStringFunc(src, regex string, replace func(s string) string) (string, error) {
var err error
if re, ok := regexArray[regex]; ok {
return re.ReplaceAllStringFunc(src, replace), nil
}
if re, err := regexp.Compile(regex); err == nil {
regexArray[regex] = re
return re.ReplaceAllStringFunc(src, replace), nil
}
return "", err
}
// FindAllString returns a slice of all strings holding the text of the leftmost
// match in src of the regular expression. If there is no match, the return value is nil.
// It will be empty if the regular expression successfully matches an empty string.
func FindAllString(src, regex string) ([]string, error) {
var err error
if re, ok := regexArray[regex]; ok {
return re.FindAllString(src, -1), nil
}
if re, err := regexp.Compile(regex); err == nil {
regexArray[regex] = re
return re.FindAllString(src, -1), nil
}
return []string{}, err
}
// FindAllStringSubmatch returns a slice of a slice of strings holding the text of the
// leftmost match of the regular expression in src and the matches.
// A return value of nil indicates no match.
func FindAllStringSubmatch(src, regex string) ([][]string, error) {
var err error
if re, ok := regexArray[regex]; ok {
return re.FindAllStringSubmatch(src, -1), nil
}
if re, err := regexp.Compile(regex); err == nil {
regexArray[regex] = re
return re.FindAllStringSubmatch(src, -1), nil
}
return [][]string{}, err
}
func Count() int {
return len(regexArray)
}
func Cleanup() {
regexArray = make(map[string]*regexp.Regexp)
}
// Package rss provides a RSS parser
package rss
import (
"encoding/xml"
"net/http"
"simonwaldherr.de/go/golibs/as"
"time"
)
// Client is a http client to crawl the feed from (#)
type Client interface {
Get(url string) (resp *http.Response, err error)
}
// ItemEnclosure contains an URL and its MIME-Type of an episode file
type ItemEnclosure struct {
URL string `xml:"url,attr"`
Type string `xml:"type,attr"`
}
// Item represents an episode with all of its content
type Item struct {
Title string `xml:"title"`
Link string `xml:"link"`
Comments string `xml:"comments"`
PubDate string `xml:"pubDate"`
GUID string `xml:"guid"`
Category []string `xml:"category"`
Enclosure []ItemEnclosure `xml:"enclosure"`
Description string `xml:"description"`
Content string `xml:"content"`
}
// Image contains the URL and title of the podcast logo
type Image struct {
URL string `xml:"url"`
Title string `xml:"title"`
}
// Main struct represents the root of the RSS feed
type Main struct {
Title string `xml:"title"`
Link string `xml:"link"`
Copyright string `xml:"copyright"`
Description string `xml:"description"`
Generator string `xml:"generator"`
Language string `xml:"language"`
LastBuildDate string `xml:"lastBuildDate"`
Items []Item `xml:"item"`
Image Image `xml:"image"`
}
// Read reads from an URL with the Golang default http client and returns rss.Main
func Read(url string) (*Main, error) {
return ReadCustomClient(url, http.DefaultClient)
}
// ReadCustomClient allows to read with an custom http client
func ReadCustomClient(url string, client Client) (*Main, error) {
r, err := client.Get(url)
if err != nil {
return nil, err
}
defer func() {
err = r.Body.Close()
}()
xmlDecoder := xml.NewDecoder(r.Body)
var rss struct {
Main Main `xml:"channel"`
}
if err = xmlDecoder.Decode(&rss); err != nil {
return nil, err
}
return &rss.Main, nil
}
// Len returns the episode count
func (rss *Main) Len() int {
return len(rss.Items)
}
// Time returns the specified last build date as time.Time
func (rss *Main) Time() time.Time {
return as.Time(rss.LastBuildDate)
}
// Time returns the specified publication date as time.Time
func (episode *Item) Time() time.Time {
return as.Time(episode.PubDate)
}
// Generate SSL Certs via this simple to use ssl package.
package ssl
import (
"bufio"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log"
"math/big"
"os"
"time"
)
const (
day = time.Hour * 24
year = day * 365
)
func Check(certPath string, keyPath string) error {
if _, err := os.Stat(certPath); os.IsNotExist(err) {
return err
} else if _, err := os.Stat(keyPath); os.IsNotExist(err) {
return err
}
return nil
}
func Generate(options map[string]string) error {
var err error
var certOut *os.File
var derBytes []byte
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Printf("failed to generate private key: %s", err)
return err
}
notBefore := time.Now().Add(day * -1)
notAfter := notBefore.Add(year * 2)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
log.Printf("failed to generate serial number: %s", err)
return err
}
scanner := bufio.NewScanner(os.Stdin)
var certPath string
if options["certPath"] == "" {
fmt.Print("# Cert filename: ")
scanner.Scan()
certPath = scanner.Text()
} else {
certPath = options["certPath"]
}
var keyPath string
if options["keyPath"] == "" {
fmt.Print("# Key filename: ")
scanner.Scan()
keyPath = scanner.Text()
} else {
keyPath = options["keyPath"]
}
var countryName string
if options["countryName"] == "" {
fmt.Print("# Country Name (2 letter code) [AU]: ")
scanner.Scan()
countryName = scanner.Text()
} else {
countryName = options["countryName"]
}
var provinceName string
if options["provinceName"] == "" {
fmt.Print("# State or Province Name (full name) [Some-State]: ")
scanner.Scan()
provinceName = scanner.Text()
} else {
provinceName = options["provinceName"]
}
var organizationName string
if options["organizationName"] == "" {
fmt.Print("# Organization Name (eg, company) [Lorem Ipsum Inc]: ")
scanner.Scan()
organizationName = scanner.Text()
} else {
organizationName = options["organizationName"]
}
var commonName string
if options["commonName"] == "" {
fmt.Print("# Common Name (eg, YOUR name) [localhost]: ")
scanner.Scan()
commonName = scanner.Text()
} else {
commonName = options["commonName"]
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Country: []string{string(countryName)},
Organization: []string{string(organizationName)},
Province: []string{string(provinceName)},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
DNSNames: []string{string(commonName)},
IsCA: true,
}
if derBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv); err != nil {
log.Printf("Failed to create certificate: %s", err)
return err
}
if certOut, err = os.Create(certPath); err != nil {
log.Printf("failed to open "+certPath+" for writing: %s", err)
return err
}
if err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
return err
}
if err = certOut.Close(); err != nil {
return err
}
log.Printf("written %v\n", certPath)
keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Print("failed to open "+keyPath+" for writing:", err)
return err
}
if err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
return err
}
if err = keyOut.Close(); err != nil {
return err
}
log.Printf("written %v\n", keyPath)
return nil
}
// stack implements a stack with "last in, first out" functionality.
// it also provides a ring memory type which overrides itself after n write ops.
package stack
type Stype int
const (
NIL Stype = iota
LiFo
FiFo
)
// struct Stack contains nodes as slice of interfaces
// and a counter for the current position.
type Stack struct {
nodes []interface{}
count int
stype Stype
}
// Lifo returns a pointer to a new stack.
func Lifo() *Stack {
return &Stack{
stype: LiFo,
}
}
func Fifo() *Stack {
return &Stack{
stype: FiFo,
}
}
func (s *Stack) Unset() {
*s = Stack{
nodes: []interface{}{},
count: 0,
stype: NIL,
}
}
func (s *Stack) ToFifo() *Stack {
var x *Stack
array := Fifo()
if s.stype == FiFo {
x = Fifo()
*x = *s
for s.Len() > 0 {
array.Push(s.Pop())
}
}
if s.stype == LiFo {
x = Lifo()
*x = *s
helper := Lifo()
for s.Len() > 0 {
helper.Push(s.Pop())
}
for helper.Len() > 0 {
array.Push(helper.Pop())
}
}
*s = *x
return array
}
func (s *Stack) ToLifo() *Stack {
var x *Stack
array := Lifo()
if s.stype == FiFo {
x = Fifo()
*x = *s
for s.Len() > 0 {
array.Push(s.Pop())
}
}
if s.stype == LiFo {
x = Lifo()
*x = *s
helper := Lifo()
for s.Len() > 0 {
helper.Push(s.Pop())
}
for helper.Len() > 0 {
array.Push(helper.Pop())
}
}
*s = *x
return array
}
func (s *Stack) Val() []interface{} {
var a *Stack
var r []interface{}
if s.stype == FiFo {
a = s.ToFifo()
} else if s.stype == LiFo {
a = s.ToLifo()
}
for s.Len() > 0 {
r = append(r, s.Pop())
}
*s = *a
return r
}
// Push adds a value to the Stack
func (s *Stack) Push(n interface{}) {
if s.stype == LiFo {
s.nodes = append(s.nodes[:s.count], n)
s.count++
} else if s.stype == FiFo {
s.nodes = append(s.nodes, n)
}
}
func (s *Stack) Add(n interface{}) {
s.Push(n)
}
// Pop returns the last added value and decrease the position counter.
func (s *Stack) Pop() interface{} {
if s.stype == LiFo {
if s.count == 0 {
return ""
}
s.count--
return s.nodes[s.count]
}
if s.stype == FiFo {
if s.count == len(s.nodes) {
return ""
}
s.count++
return s.nodes[s.count-1]
}
return ""
}
func (s *Stack) Get() interface{} {
return s.Pop()
}
// Len returns the current position in the Stack.
func (s *Stack) Len() int {
if s.stype == LiFo {
return s.count
}
if s.stype == FiFo {
return len(s.nodes) - s.count
}
return -1
}
func (s *Stack) IsEmpty() bool {
if s.Len() == 0 {
return true
}
return false
}
// Ring returns a pointer to a new ring.
func Ring() *Rings {
return &Rings{}
}
// struct Rings contains nodes as slice of strings,
// count for the current ring position,
// xcount for the total amount of added entries
// and size for the maximum size of the "Ring".
type Rings struct {
nodes []string
count int
xcount int
size int
}
// SetSize sets the maximum size of the Ring,
// this size must be greater then the current counter.
func (r *Rings) SetSize(i int) {
if i > r.xcount {
r.size = i
}
}
// Init sets the default values of the Ring.
func (r *Rings) Init(i int) {
r.size = i
r.count = -1
r.xcount = -1
}
// GetSize returns the max size of the Ring.
func (r *Rings) GetSize() int {
return r.size
}
// Push adds a string to the Ring and returns it position
func (r *Rings) Push(n string) int {
r.count++
r.xcount++
if r.count > r.size {
r.count = 0
}
if r.xcount > r.count {
r.nodes[r.count] = n
} else {
r.nodes = append(r.nodes[:r.count], n)
}
return r.count
}
// Get returns a slice of strings from the given
// to the current position
func (r *Rings) Get(from int) []string {
ret := make([]string, r.size)
var i int
for from != r.count {
ret[i] = string(r.nodes[from])
from++
i++
if from > r.size {
from = 0
}
}
return ret
}
// Pos returns the current position and the
// number of overall added values
func (r *Rings) Pos() (int, int) {
return r.count, r.xcount
}
package structs
import (
"reflect"
)
func Reflect(sstruct interface{}) map[string]interface{} {
attrs := make(map[string]interface{})
v := reflect.ValueOf(sstruct)
t := reflect.TypeOf(sstruct)
attrs = ReflectHelper(v, t, 0, func(name string, vtype string, value interface{}, depth int) {
})
return attrs
}
func ReflectHelper(v reflect.Value, t reflect.Type, depth int, handler func(string, string, interface{}, int)) map[string]interface{} {
attrs := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
e := v.Field(i)
f := t.Field(i)
handler(f.Name, e.Type().String(), e.Interface(), depth)
if e.Kind().String() == "struct" {
attrs[f.Name] = ReflectHelper(e, e.Type(), depth+1, handler)
} else {
attrs[f.Name] = e.Type().String()
}
}
return attrs
}
// xmath is a package with math functions.
// Besides a few standard formulas it contains various mean algorithms.
package xmath
import (
"math"
"reflect"
"simonwaldherr.de/go/golibs/as"
"sort"
)
// Sqrt calculates the square root of n.
func Sqrt(n int64) int64 {
var t int64
var b int64
var r int64
t = int64(n)
p := int64(1 << 30)
for p > t {
p >>= 2
}
for ; p != 0; p >>= 2 {
b = r | p
r >>= 1
if t >= b {
t -= b
r |= p
}
}
return int64(r)
}
// Prime returns the nth prime number as int.
func Prime(n int) int {
var primeList = []int{2}
isPrime := 1
num := 3
sqrtNum := 0
for len(primeList) < n {
sqrtNum = int(Sqrt(int64(num)))
for i := 0; i < len(primeList); i++ {
if num%primeList[i] == 0 {
isPrime = 0
}
if primeList[i] > sqrtNum {
i = len(primeList)
}
}
if isPrime == 1 {
primeList = append(primeList, num)
} else {
isPrime = 1
}
num = num + 2
}
return primeList[n-1]
}
// Deg2Rad returns the rad of a deg.
func Deg2Rad(deg float64) float64 {
return (deg * math.Pi) / 180
}
// Rad2Deg returns the deg of a rad.
func Rad2Deg(rad float64) float64 {
return (rad * 180) / math.Pi
}
// Round returns a rounded int from a float64.
// It rounds via "Round half away from zero".
func Round(v float64) int {
if v < 0 {
return int(math.Ceil(v - 0.5))
}
return int(math.Floor(v + 0.5))
}
// Round returns a rounded float64 from a float64
// with d digits after the point. It rounds via
// "Round half away from zero".
func FloatRound(v float64, d int) float64 {
pow := math.Pow(10, float64(d))
return float64(Round(v*pow)) / pow
}
// Count returns the length of any slice (like len()).
func Count(val interface{}) int {
slice := reflect.ValueOf(val)
return slice.Len()
}
// Sum returns the sum from a slice of Values as float64.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Sum(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
out := make([]float64, c)
for i := 0; i < c; i++ {
out[i] = as.Float(slice.Index(i).Interface())
}
var sum float64
for _, value := range out {
sum = sum + value
}
return sum
}
// Min returns the smallest number from a slice of Values as float64.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Min(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
out := make([]float64, c)
for i := 0; i < c; i++ {
out[i] = as.Float(slice.Index(i).Interface())
}
min := out[0]
for _, value := range out {
if value < min {
min = value
}
}
return min
}
// Max returns the greatest number from a slice of Values as float64.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Max(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
out := make([]float64, c)
for i := 0; i < c; i++ {
out[i] = as.Float(slice.Index(i).Interface())
}
max := out[0]
for _, value := range out {
if value > max {
max = value
}
}
return max
}
type Meantype int
const (
ArithmeticMean Meantype = iota
GeometricMean
HarmonicMean
MedianMean
RmsMean
Default
)
func Mean(val interface{}, t Meantype) float64 {
switch t {
case ArithmeticMean:
return Arithmetic(val)
case GeometricMean:
return Geometric(val)
case HarmonicMean:
return Harmonic(val)
case MedianMean:
return Median(val)
case RmsMean:
return Rootmeansquare(val)
}
return Arithmetic(val)
}
// Median returns the median from a slice of Values as float64.
// The median is the numerical value separating the higher half
// of a data sample from the lower half. The median of a list of
// numbers can be found by arranging all the observations from
// lowest value to highest value and picking the middle one.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Median(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
out := make([]float64, c)
for i := 0; i < c; i++ {
out[i] = as.Float(slice.Index(i).Interface())
}
sort.Float64s(out)
if c%2 == 1 {
return out[c/2]
}
return (out[c/2] + out[c/2-1]) / 2
}
// Arithmetic returns the arithmetic mean from a slice of Values as float64.
// The arithmetic mean or simply the mean or average when the context is clear,
// is the sum of a list of numbers divided by the number of numbers
// in the list.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Arithmetic(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
out := make([]float64, c)
for i := 0; i < c; i++ {
out[i] = as.Float(slice.Index(i).Interface())
}
return (Sum(out) / float64(len(out)))
}
// Rootmeansquare returns the root mean square from a slice of Values as float64.
// The root mean square is the root value of the sum of the squared value of a
// list of numbers divided by the number of numbers in the list.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Rootmeansquare(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
out := make([]float64, c)
for i := 0; i < c; i++ {
out[i] = math.Pow(as.Float(slice.Index(i).Interface()), 2)
}
return math.Sqrt(Sum(out) / float64(len(out)))
}
// Harmonic returns the harmonic mean from a slice of Values as float64.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Harmonic(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
s := float64(0)
for i := 0; i < c; i++ {
s = s + 1/as.Float(slice.Index(i).Interface())
}
return (float64(c) * 1 / s)
}
// Geometric returns the geometric mean from a slice of Values as float64.
// The geometric mean is a type of mean or average, which indicates the central
// tendency or typical value of a set of numbers by using the product of their
// values (as opposed to the arithmetic mean which uses their sum). The
// geometric mean is defined as the nth root of the product of n numbers.
// It uses "as" (simonwaldherr.de/go/golibs/as) to
// convert given values to floats.
func Geometric(val interface{}) float64 {
slice := reflect.ValueOf(val)
c := slice.Len()
var m float64 = 1
for i := 0; i < c; i++ {
m = m * as.Float(slice.Index(i).Interface())
}
return float64(math.Pow(float64(m), 1/float64(c)))
}
// Even tells if a number is even
func Even(number int) bool {
return number%2 == 0
}
// Odd tells if a number is odd
func Odd(number int) bool {
return !Even(number)
}
package xtime
import (
"time"
)
var conv = map[rune]string{
'a': "Mon",
'A': "Monday",
'b': "Jan",
'B': "January",
'd': "02",
'D': "01-02-2006",
'F': "2006-01-02",
'H': "15",
'I': "03",
'L': ".000",
'm': "01",
'M': "04",
'p': "PM",
'S': "05",
'x': "2006-01-02",
'X': "15:04:05",
'y': "06",
'Y': "2006",
'z': "-0700",
'Z': "MST",
'0': "",
'%': "",
}
// StrfTime implements a subset of strftime
// http://man7.org/linux/man-pages/man3/strftime.3.html
func StrfTime(format string, t time.Time) string {
ret := make([]byte, 0, len(format))
for i := 0; i < len(format); i++ {
if format[i] == '%' {
if layout, ok := conv[rune(format[i+1])]; ok {
ret = append(ret, []byte(t.Format(layout))...)
i++
} else {
ret = append(ret, format[i])
}
} else {
ret = append(ret, format[i])
}
}
return string(ret)
}
// Fmt (xtime.Fmt) is an alias for StrfTime
func Fmt(format string, t time.Time) string {
return StrfTime(format, t)
}
// FmtNow is like StrfTime, but automatically with the current local time
func FmtNow(format string) string {
t := time.Now()
return StrfTime(format, t)
}