func ResizeImage(from string, to string, size string) error {
	folder := filepath.Dir(to)
	if _, err := os.Stat(folder); os.IsNotExist(err) {
		err = os.MkdirAll(folder, 0775)
		if err != nil {
			return err
		}
	}
	var args = []string{
		"--size", size,
		"--output", to,
		from,
	}
	path, err := exec.LookPath("vipsthumbnail")
	if err != nil {
		return err
	}
	cmd := exec.Command(path, args...)
	err = cmd.Run()
	if err != nil {
		log.Error("Can not resize image. args:"+fmt.Sprint(args), "")
		return errors.New("Can not resize image " + from + ": " + err.Error())
	}
	return nil
}
Generate a resized image
package util
import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"net/smtp"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
	"github.com/digimakergo/digimaker/core/log"
	"github.com/google/uuid"
	"github.com/rs/xid"
	"golang.org/x/crypto/bcrypt"
)
func UnmarshalData(filepath string, v interface{}) error {
	file, err := os.Open(filepath)
	if err != nil {
		log.Error("Error when loading content definition: "+err.Error(), "")
		return err
	}
	byteValue, _ := ioutil.ReadAll(file)
	err = json.Unmarshal(byteValue, v)
	if err != nil {
		log.Error("Error when loading definition "+filepath+": "+err.Error(), "")
		return err
	}
	defer file.Close()
	return nil
}
func GenerateUID() string {
	guid := xid.New()
	guidStr := guid.String()
	return guidStr
}
func GenerateGUID() string {
	uuid := uuid.New()
	return uuid.String()
}
func ArrayStrToInt(strArray []string) ([]int, error) {
	size := len(strArray)
	var result = make([]int, size)
	for i, str := range strArray {
		value, err := strconv.Atoi(str)
		result[i] = value
		if err != nil {
			return []int{}, err
		}
	}
	return result, nil
}
var varBrakets = []string{"{", "}"}
func GetStrVar(str string) []string {
	left := varBrakets[0]
	right := varBrakets[1]
	r, _ := regexp.Compile(left + `(\w+)` + right)
	match := r.FindAllStringSubmatch(str, -1)
	result := []string{}
	for i := range match {
		result = append(result, match[i][1])
	}
	return result
}
func ReplaceStrVar(str string, values map[string]string) string {
	result := str
	for key := range values {
		value := values[key]
		old := varBrakets[0] + key + varBrakets[1]
		result = strings.ReplaceAll(result, old, value)
	}
	return result
}
func UpperName(input string) string {
	arr := strings.Split(input, "_")
	for i := range arr {
		arr[i] = strings.Title(arr[i])
	}
	return strings.Join(arr, "")
}
func InterfaceToStringArray(input []interface{}) []string {
	result := make([]string, len(input))
	for i, value := range input {
		result[i] = value.(string)
	}
	return result
}
func NameToIdentifier(input string) string {
	lowerStr := strings.ToLower(strings.TrimSpace(input))
	reg, _ := regexp.Compile("[^a-z0-9]+")
	result := reg.ReplaceAllString(lowerStr, "-")
	return result
}
func StripSQLPhrase(str string) string {
	reg, _ := regexp.Compile("[^a-z0-9A-Z., _]+")
	result := reg.ReplaceAllString(str, "")
	return result
}
func MatchCondition(conditions map[string]interface{}, target map[string]interface{}) (bool, []string) {
	matchLog := []string{}
	for key, conditionValue := range conditions {
		matchResult := false
		realValue, ok := target[key]
		if !ok {
			matchResult = false
			matchLog = append(matchLog, "Mismatch since key "+key+" doesn't exist in target.")
		} else {
			if realValue == nil {
				matchResult = true  
				matchLog = append(matchLog, "Matched on "+key+"since the real value is nil")
			} else if str, ok := conditionValue.(string); ok && str == "*" {
				matchResult = true
			} else {
				switch conditionValue.(type) {
				case []interface{}:
					for _, item := range conditionValue.([]interface{}) {
						errorLog := ""
						matchResult, errorLog = matchItem(item, realValue)
						if matchResult {
							break
						}
						if errorLog != "" {
							matchLog = append(matchLog, errorLog)
						}
					}
				default:
					errorLog := ""
					matchResult, errorLog = matchItem(conditionValue, realValue)
					if errorLog != "" {
						matchLog = append(matchLog, errorLog)
					}
				}
			}
			if !matchResult {
				matchLog = append(matchLog, "Mismatch on "+key+", expecting: "+fmt.Sprint(conditionValue)+", real: "+fmt.Sprint(realValue))
			} else {
				matchLog = append(matchLog, "Matched on "+key)
			}
		}
		if !matchResult {
			return false, matchLog
		}
	}
	return true, matchLog
}
func matchItem(conditionValue interface{}, realValue interface{}) (bool, string) {
	result := false
	if _, ok := conditionValue.(float64); ok {
		conditionValue = int(conditionValue.(float64))
	}
	switch conditionValue.(type) {
	case string:
		switch realValue.(type) {
		case string:
			result = conditionValue.(string) == realValue.(string)
		case []string:
			result = Contains(realValue.([]string), conditionValue.(string))
		default:
			return false, "Target is not string/[]string"
		}
	case int:
		switch realValue.(type) {
		case int:
			result = conditionValue.(int) == realValue.(int)
		case []int:  
			result = ContainsInt(realValue.([]int), conditionValue.(int))
		default:
			return false, "Target is not int/[]int"
		}
	default:
		return false, "unsupported condition type:" + fmt.Sprintf("%T", conditionValue)
	}
	return result, ""
}
func Split(str string, seperator ...string) []string {
	sep := ""
	str = strings.TrimSpace(str)
	if len(seperator) == 0 {
		sep = ","
	} else {
		sep = seperator[0]
	}
	arr := strings.Split(str, sep)
	for i, value := range arr {
		arr[i] = strings.TrimSpace(value)
	}
	return arr
}
func HashPassword(password string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), 14)  
	return string(hash), err
}
func MatchPassword(password string, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	if err != nil {
		return false
	}
	return true
}
func SendMail(to []string, subject, body string) error {
	from := GetConfig("general", "send_from", "dm")
	r := strings.NewReplacer("\r\n", "", "\r", "", "\n", "", "%0a", "", "%0d", "")
	addr := "127.0.0.1:25"
	c, err := smtp.Dial(addr)
	if err != nil {
		return err
	}
	defer c.Close()
	if err = c.Mail(r.Replace(from)); err != nil {
		return err
	}
	for i := range to {
		to[i] = r.Replace(to[i])
		if err = c.Rcpt(to[i]); err != nil {
			return err
		}
	}
	w, err := c.Data()
	if err != nil {
		return err
	}
	msg := "To: " + strings.Join(to, ",") + "\r\n" +
		"From: " + from + "\r\n" +
		"Subject: " + subject + "\r\n" +
		"Content-Type: text/html; charset=\"UTF-8\"\r\n" +
		"Content-Transfer-Encoding: base64\r\n" +
		"\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
	_, err = w.Write([]byte(msg))
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}
	return c.Quit()
}
func RandomStr(n int) []byte {
	rand.Seed(time.Now().UTC().UnixNano())
	characters := "abcdefghijklmnopqrstuvwxyz"
	str := []byte("")
	for i := 0; i < n; i++ {
		j := rand.Intn(len(characters))
		str = append(str, characters[j])
	}
	return str
}