1
0
Fork 0

Initial commit

This commit is contained in:
Gregory Eremin 2015-01-18 17:50:03 +07:00
commit 30c24998be
7 changed files with 296 additions and 0 deletions

0
.gitignore vendored Normal file
View File

1
README.md Normal file
View File

@ -0,0 +1 @@
# Confection

121
config.go Normal file
View File

@ -0,0 +1,121 @@
package confection
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"strings"
)
type (
config struct {
config interface{}
}
configField struct {
Path string `json:"path"`
IsRequired bool `json:"is_required"`
IsReadonly bool `json:"is_readonly"`
Title string `json:"title"`
Description string `json:"description"`
Options []string `json:"options"`
}
)
const (
tJson = "json"
tTitle = "title"
tDescription = "description"
tAttrs = "attrs"
tOptions = "options"
aRequired = "required"
aReadonly = "readonly"
sep = ","
)
func (c *config) dump() ([]byte, error) {
var (
out bytes.Buffer
b []byte
err error
)
if b, err = json.Marshal(c.config); err != nil {
return nil, err
}
// Indent with empty prefix and four spaces
if err = json.Indent(&out, b, "", " "); err != nil {
return nil, err
}
return out.Bytes(), nil
}
func (c *config) load(b []byte) (err error) {
return
}
// TODO: function draft, needs refactor
func (c *config) meta(prefix string) []*configField {
var (
fields = []*configField{}
cval = reflect.ValueOf(c.config)
typ = reflect.TypeOf(c.config)
kind = cval.Kind()
)
if kind != reflect.Struct {
panic(fmt.Errorf("Config is expected to be a Struct, not %s", kind.String()))
}
for i := 0; i < cval.NumField(); i++ {
var (
field = typ.Field(i)
val = cval.Field(i)
jsonKey = field.Tag.Get(tJson)
path = strings.Join([]string{prefix, jsonKey}, "/")
title = field.Tag.Get(tTitle)
description = field.Tag.Get(tDescription)
attrs = strings.Split(field.Tag.Get(tAttrs), sep)
options = strings.Split(field.Tag.Get(tOptions), sep)
cf = &configField{
Path: path,
Title: title,
Description: description,
}
)
// Skip field if no tags are set
if title == "" && len(attrs) == 0 && len(options) == 0 {
continue
}
// Substitute field name for title if none set
if title == "" {
cf.Title = field.Name
}
for _, attr := range attrs {
if attr == aRequired {
cf.IsRequired = true
}
if attr == aReadonly {
cf.IsReadonly = true
}
}
fields = append(fields, cf)
// Recursion here
if val.Kind() == reflect.Struct {
subconf := &config{
config: val.Interface(),
}
fields = append(fields, subconf.meta(path)...)
}
}
return fields
}

60
config_file.go Normal file
View File

@ -0,0 +1,60 @@
package confection
import (
"fmt"
"io/ioutil"
"os"
"path"
)
type (
configFile struct {
path string
}
)
func (cf *configFile) write(b []byte) (err error) {
var (
fd *os.File
n int // Bytes written successfully
)
if cf.isExist() {
if err = cf.mkdirp(); err != nil {
return
}
fd, err = os.Create(cf.path)
} else {
fd, err = os.Open(cf.path)
}
if err != nil {
return
}
defer fd.Close()
n, err = fd.Write(b)
if err == nil && n != len(b) {
return fmt.Errorf("Failed to write config file: written %d/%d bytes", n, len(b))
}
return
}
func (cf *configFile) read() ([]byte, error) {
if cf.isExist() {
return ioutil.ReadFile(cf.path)
} else {
return nil, fmt.Errorf("Config file does not exist")
}
}
func (cf *configFile) isExist() bool {
_, err := os.Stat(cf.path)
return os.IsExist(err)
}
func (cf *configFile) mkdirp() error {
dir := path.Dir(cf.path)
return os.MkdirAll(dir, 0755)
}

35
demo/demo.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"flag"
"github.com/localhots/confection"
)
type (
Config struct {
AppName string `json:"app_name" attrs:"required" title:"Application Name"`
BuildNumber int `json:"build_number" attrs:"readonly" title:"Build Number"`
EnableSignIn bool `json:"enable_sign_in" title:"Enable Sign-In"`
DatabaseDriver string `json:"database_driver" title:"Database Driver" options:"mysql,postgresql,mssql"`
DatabaseConfig DatabaseConfig `json:"database_config"`
}
DatabaseConfig struct {
Hostname string `json:"hostname"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Database string `json:"database" attrs:"required"`
}
)
func main() {
confection.Setup()
flag.Parse()
conf := Config{
DatabaseConfig: DatabaseConfig{},
}
manager := confection.New(conf)
manager.StartServer()
}

46
manager.go Normal file
View File

@ -0,0 +1,46 @@
package confection
import (
"flag"
"sync"
)
type (
Manager struct {
mux *sync.Mutex
conf *config
file *configFile
}
)
var (
configPath string
serverPort int
)
func Setup() {
flag.StringVar(&configPath, "config", "config.json", "Path to config file")
flag.IntVar(&serverPort, "config-port", 5050, "Config manager http port")
}
func New(conf interface{}) *Manager {
mgr := &Manager{
mux: &sync.Mutex{},
conf: &config{
config: conf,
},
file: &configFile{
path: configPath,
},
}
return mgr
}
func (m *Manager) StartServer() {
srv := &server{
manager: m,
port: serverPort,
}
srv.start()
}

33
server.go Normal file
View File

@ -0,0 +1,33 @@
package confection
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
)
type (
server struct {
manager *Manager
port int
}
)
func (s *server) start() {
portStr := ":" + strconv.Itoa(s.port)
fmt.Println("Configuration server is available at http://127.0.0.1" + portStr)
if err := http.ListenAndServe(portStr, s); err != nil {
panic(err)
}
}
func (s *server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
jsn, err := json.Marshal(s.manager.conf.meta(""))
if err != nil {
panic(err)
}
w.Write(jsn)
}