Copied over from Gogs Repo

This commit is contained in:
Brian Buller 2016-02-09 08:32:36 -06:00
parent 2ba7356ea3
commit 9dce529fe5
26 changed files with 554 additions and 0 deletions

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# user-config
A go library for easily managing config files/directories in your XDGConfig directory

128
config.go Normal file
View File

@ -0,0 +1,128 @@
// Package userConfig eases the use of config files in a user's home directory
package userConfig
import (
"errors"
"os"
"strings"
"gogs.bullercodeworks.com/brian/user-config/ext/go-xdg"
)
// Config is a stuct for managing the config
type Config struct {
name string
generalConfig *GeneralConfig
}
// NewConfig generates a Config struct
func NewConfig(name string) (*Config, error) {
c := &Config{name: name}
if err := c.Load(); err != nil {
return c, err
}
return c, nil
}
// Set at the config level sets a value in the <c.name>.conf file
func (c *Config) Set(k, v string) error {
return c.generalConfig.Set(k, v)
}
// Get at the config level retrieves a value from the <c.name>.conf file
func (c *Config) Get(k string) string {
return c.generalConfig.Get(k)
}
// GetConfigPath just returns the config path
func (c *Config) GetConfigPath() string {
return c.generalConfig.Path
}
// Load loads config files into the config
func (c *Config) Load() error {
var err error
if strings.TrimSpace(c.name) == "" {
return errors.New("Invalid Config Name: " + c.name)
}
var cfgPath string
cfgPath = xdg.Config.Dirs()[0]
if cfgPath != "" {
cfgPath = cfgPath + "/" + c.name
if err = c.verifyOrCreateDirectory(cfgPath); err != nil {
return err
}
// We always have a <name>.conf file
//cfgPath = cfgPath + "/" + c.name + ".conf"
}
// Load general config
if c.generalConfig, err = NewGeneralConfig(c.name, cfgPath); err != nil {
return err
}
return nil
}
// Save writes the config to file(s)
func (c *Config) Save() error {
if c.generalConfig == nil {
return errors.New("Bad setup.")
}
return c.generalConfig.Save()
/*
var cfgPath string
var configLines []string
//configLines = append(configLines, "server="+client.ServerAddr)
//configLines = append(configLines, "key="+client.ServerKey)
cfgPath = os.Getenv("HOME")
if cfgPath != "" {
cfgPath = cfgPath + "/.config"
if err := c.verifyOrCreateDirectory(cfgPath); err != nil {
return err
}
cfgPath = cfgPath + "/" + c.name
}
if cfgPath != "" {
file, err := os.Create(cfgPath)
if err != nil {
// Couldn't load config even though one was specified
return err
}
defer file.Close()
w := bufio.NewWriter(file)
for _, line := range configLines {
fmt.Fprintln(w, line)
}
if err = w.Flush(); err != nil {
return err
}
}
return nil
*/
}
// verifyOrCreateDirectory is a helper function for building an
// individual directory
func (c *Config) verifyOrCreateDirectory(path string) error {
var tstDir *os.File
var tstDirInfo os.FileInfo
var err error
if tstDir, err = os.Open(path); err != nil {
if err = os.Mkdir(path, 0755); err != nil {
return err
}
if tstDir, err = os.Open(path); err != nil {
return err
}
}
if tstDirInfo, err = tstDir.Stat(); err != nil {
return err
}
if !tstDirInfo.IsDir() {
return errors.New(path + " exists and is not a directory")
}
// We were able to open the path and it was a directory
return nil
}

82
config_file.go Normal file
View File

@ -0,0 +1,82 @@
// Package userConfig eases the use of config files in a user's home directory
package userConfig
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"strings"
"github.com/BurntSushi/toml"
)
// GeneralConfig is the basic config structure
// All configs make with package userConfig will have this file
type GeneralConfig struct {
Name string `toml:"-"`
Path string `toml:"-"`
ConfigFiles []string `toml:"additional_config"`
RawFiles []string `toml:"raw_files"`
Values map[string]string `toml:"general"`
}
// NewGeneralConfig generates a General Config struct
func NewGeneralConfig(name, path string) (*GeneralConfig, error) {
gf := &GeneralConfig{Name: name, Path: path}
gf.ConfigFiles = []string{}
gf.RawFiles = []string{}
gf.Values = make(map[string]string)
if err := gf.Load(); err != nil {
return gf, err
}
return gf, nil
}
// Load loads config files into the config
func (gf *GeneralConfig) Load() error {
if strings.TrimSpace(gf.Name) == "" || strings.TrimSpace(gf.Path) == "" {
return errors.New("Invalid ConfigFile Name: " + gf.Path + "/" + gf.Name)
}
// Config files end with .conf
cfgPath := gf.Path + "/" + gf.Name + ".conf"
tomlData, err := ioutil.ReadFile(cfgPath)
if err != nil {
return err
}
if _, err := toml.Decode(string(tomlData), &gf); err != nil {
return err
}
return nil
}
// Save writes the config to file(s)
func (gf *GeneralConfig) Save() error {
buf := new(bytes.Buffer)
cfgPath := gf.Path + "/" + gf.Name + ".conf"
fmt.Println("Writing Config File: " + cfgPath)
if err := toml.NewEncoder(buf).Encode(gf); err != nil {
return err
}
fmt.Println("Writing Config File: " + buf.String())
return ioutil.WriteFile(cfgPath, buf.Bytes(), 0644)
}
// Set sets a key/value pair in gf, if unable to save, revert to old value
// (and return the error)
func (gf *GeneralConfig) Set(k, v string) error {
oldVal := gf.Values[k]
gf.Values[k] = v
if err := gf.Save(); err != nil {
gf.Values[k] = oldVal
return err
}
return nil
}
// Get gets a key/value pair from gf
func (gf *GeneralConfig) Get(k string) string {
return gf.Values[k]
}

3
ext/go-xdg/.bzr/README Normal file
View File

@ -0,0 +1,3 @@
This is a Bazaar control directory.
Do not change any files in this directory.
See http://bazaar.canonical.com/ for more information about Bazaar.

View File

@ -0,0 +1 @@
Bazaar-NG meta directory, format 1

View File

@ -0,0 +1 @@
parent_location = http://bazaar.launchpad.net/~chipaca/go-xdg/trunk/

View File

@ -0,0 +1 @@
Bazaar Branch Format 7 (needs bzr 1.6)

View File

@ -0,0 +1 @@
10 john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa

View File

View File

@ -0,0 +1 @@
BZR conflict list format 1

Binary file not shown.

View File

@ -0,0 +1 @@
Bazaar Working Tree Format 6 (bzr 1.14)

View File

View File

@ -0,0 +1 @@
Bazaar repository format 2a (needs bzr 1.16 or later)

View File

@ -0,0 +1,8 @@
B+Tree Graph Index 2
node_ref_lists=0
key_elements=1
len=10
row_lengths=1
<EFBFBD>Ò;ƒ0 àÔ{
.àŒeùE±3{cGbp 8N¿<4E>†tШüæ×/<2F>ï‡ÿ Þ”?·Xw×à»1vÖt±k¬ W[Â(p
L 犸Ĥ<E28093>«.µÑ…Ë…³ £eL qc A™VÏ<56>{Ð…ß1ÛР1„„Í1åup“(”°ì Ì?ñÎaÈ%émcíÝÊ&¯}ù,¾­¡2!€ s!ÉT;åÇ<>)c<>Ý1©ä'Ý)o[ç ²Û;Þ¿ú_«ÏÖécL¢šS2FìÃûŽ}³,-³ÃŽm f´pÐT<C390>nFÌ_"=|ñJ¬vl õ¹è)ŒQŽdŠ¯~0ë9tH#b½ct+íÒ4çšRR¥Â‰Ö);DlÇÙ|u¿n—I½~Å?ìøÞÅ

Binary file not shown.

23
ext/go-xdg/LICENSE Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) 2014, John R. Lenton.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

39
ext/go-xdg/README.md Normal file
View File

@ -0,0 +1,39 @@
Go, XDG, go!
===========
This is `go-xdg`, a little library to help you use the `XDG`
[base directory spec](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html).
(There are other `XDG` specs, that might get included in time. Patches welcome.)
Sample usage
------------
Let's say you are writing an app called “frobz”. It has a config file
and a sqlite database. You'd do something like this:
configFileName, err := xdg.Config.Find("frobz/config.txt")
if err == nil {
// a config file exists! load it...
}
dbFileName, err := xdg.Data.Ensure("frobz/frobz.db")
// now the file and all its directories exist; it's up to you to
// determine if it's empty, etc.
Resources
---------
Both `Find` and `Ensure` take a `resource` to construct the path they return.
A resource is usually an application name (or a well-known shared resource
pool name, such as `icons`), followed by a filename. However nothing in the
standard nor in this library limits you to that; you may store e.g. your
application's configuration in just `$XDG_CONFIG_HOME/application.conf` (in
which case the "resource" here would be just `application.conf`), or in a
sub-directory of an application-specific directory.
License, etc.
------------
BSD simplified, © John R. Lenton, blah blah.

View File

@ -0,0 +1,103 @@
// (c) 2014 John R. Lenton. See LICENSE.
package xdg
import (
"os"
"os/user"
"path/filepath"
)
// An XDGDir holds configuration for and can be used to access the
// XDG-specified base directories relative to which user-specific files of a
// given type should be stored.
//
// Typically you wouldn't use XDGDir directly, but one of the
// predefined ones which implement the spec: Data, Config and Cache.
type XDGDir struct {
homeEnv string
homeDefault string
dirsEnv string
dirsDefault string
}
var (
Data *XDGDir // for data files.
Config *XDGDir // for configuration files.
Cache *XDGDir // for non-essential data files.
)
func init() {
// do this here to make the docs nicer
Data = &XDGDir{"XDG_DATA_HOME", ".local/share", "XDG_DATA_DIRS", "/usr/local/share:/usr/share"}
Config = &XDGDir{"XDG_CONFIG_HOME", ".config", "XDG_CONFIG_DIRS", "/etc/xdg"}
Cache = &XDGDir{"XDG_CACHE_HOME", ".cache", "", ""}
}
// Home gets the path to the given user-specific XDG directory, as specified
// (or not) by the user's environment.
func (x *XDGDir) Home() string {
dir := os.Getenv(x.homeEnv)
if dir != "" {
return dir
}
home := os.Getenv("HOME")
if home == "" {
user, err := user.Current()
if err != nil {
panic("unable to determine $HOME")
}
home = user.HomeDir
}
return filepath.Join(home, x.homeDefault)
}
// Dirs returns the preference-ordered set of base directories to search for
// files of the given type, starting with the user-specific one, as specified
// (or not) by the user's environment.
func (x *XDGDir) Dirs() []string {
dirs := []string{x.Home()}
if x.dirsEnv != "" {
xtra := os.Getenv(x.dirsEnv)
if xtra == "" {
xtra = x.dirsDefault
}
for _, path := range filepath.SplitList(xtra) {
if path != "" {
dirs = append(dirs, path)
}
}
}
return dirs
}
// Find attempts to find the path suffix in all of the known XDG directories.
// If not found, an error is returned.
func (x *XDGDir) Find(suffix string) (absPath string, err error) {
var firstError error = nil
for _, path := range x.Dirs() {
name := filepath.Join(path, suffix)
_, err = os.Stat(name)
if err == nil {
return name, nil
} else if firstError == nil {
firstError = err
}
}
return "", firstError
}
// Ensure takes the path suffix given, and ensures that a matching file exists
// in the home XDG directory. If it doesn't exist it is created. If it can't
// be created, or exists but is unreadable, an error is returned.
func (x *XDGDir) Ensure(suffix string) (absPath string, err error) {
absPath = filepath.Join(x.Home(), suffix)
err = os.MkdirAll(filepath.Dir(absPath), 0700)
if err == nil {
f, err := os.OpenFile(absPath, os.O_CREATE, 0600)
if err == nil {
f.Close()
}
}
return
}

View File

@ -0,0 +1,151 @@
// (c) 2014 John R. Lenton. See LICENSE.
package xdg
import (
. "launchpad.net/gocheck"
"os"
"path/filepath"
"strings"
"testing"
)
func TestXDGd(t *testing.T) { TestingT(t) }
type xdgdSuite struct {
home string
env1 string
val1 string
env2 string
val2 string
dir *XDGDir
}
var _ = Suite(&xdgdSuite{})
func (s *xdgdSuite) SetUpTest(c *C) {
s.home = os.Getenv("HOME")
s.env1 = "go_xdg_one"
s.env2 = "go_xdg_two"
s.val1 = "something"
s.val2 = "one:two:three"
s.dir = &XDGDir{s.env1, s.val1, s.env2, s.val2}
}
func (s *xdgdSuite) TestHomePrefersEnviron(c *C) {
err := os.Setenv(s.env1, "algo")
c.Assert(err, IsNil)
defer os.Setenv(s.env1, "")
h := s.dir.Home()
c.Check(h, Equals, "algo")
}
func (s *xdgdSuite) TestHomeUsesDefault(c *C) {
h := s.dir.Home()
c.Check(h, Matches, s.home+".*"+s.val1)
}
func (s *xdgdSuite) TestDirsPrefersEnviron(c *C) {
err := os.Setenv(s.env1, "cero")
c.Assert(err, IsNil)
defer os.Setenv(s.env1, "")
err = os.Setenv(s.env2, "uno:dos")
c.Assert(err, IsNil)
defer os.Setenv(s.env2, "")
hs := s.dir.Dirs()
c.Check(hs, DeepEquals, []string{"cero", "uno", "dos"})
}
func (s *xdgdSuite) TestDirsSkipsEmpty(c *C) {
err := os.Setenv(s.env2, "::")
c.Assert(err, IsNil)
defer os.Setenv(s.env2, "")
hs := s.dir.Dirs()
c.Check(hs, HasLen, 1)
}
func (s *xdgdSuite) TestDirsUsesDefault(c *C) {
hs := s.dir.Dirs()
c.Assert(hs, HasLen, 4)
c.Check(hs[1:], DeepEquals, strings.Split(s.val2, ":"))
c.Check(hs[0], Matches, s.home+".*"+s.val1)
}
// now repeat all the tests, but without the HOME environ.
type xdgdNoHomeSuite struct {
xdgdSuite
}
var _ = Suite(&xdgdNoHomeSuite{})
func (s *xdgdNoHomeSuite) SetUpTest(c *C) {
s.xdgdSuite.SetUpTest(c)
os.Setenv("HOME", "")
}
func (s *xdgdNoHomeSuite) TearDownTest(c *C) {
os.Setenv("HOME", s.home)
}
// and for these tests, an entirely fake HOME
type xdgdFHSuite struct {
xdgdSuite
real_home string
}
var _ = Suite(&xdgdFHSuite{})
func (s *xdgdFHSuite) SetUpTest(c *C) {
s.real_home = os.Getenv("HOME")
home := c.MkDir()
os.Setenv("HOME", home)
s.xdgdSuite.SetUpTest(c)
s.val2 = c.MkDir() + ":" + c.MkDir() + ":" + c.MkDir()
s.dir = &XDGDir{s.env1, s.val1, s.env2, s.val2}
}
func (s *xdgdFHSuite) TearDownTest(c *C) {
os.Setenv("HOME", s.real_home)
}
func (s *xdgdFHSuite) TestFind(c *C) {
vs := strings.Split(s.val2, ":")
res1 := "stuff"
exp1 := filepath.Join(s.home, s.val1, res1)
res2 := "things/that"
exp2 := filepath.Join(vs[1], res2)
res3 := "more"
exp3 := filepath.Join(vs[2], res3)
for _, d := range []string{exp1, exp2, exp3} {
err := os.MkdirAll(d, 0700)
c.Assert(err, IsNil, Commentf(d))
}
for _, it := range []struct {
res string
exp string
}{{res1, exp1}, {res2, exp2}, {res3, exp3}} {
rv, err := s.dir.Find(it.res)
c.Assert(err, IsNil)
c.Check(rv, Equals, it.exp)
}
_, err := s.dir.Find("missing")
c.Check(err, NotNil)
}
func (s *xdgdFHSuite) TestEnsureFirst(c *C) {
// creates it if missing
rv1, err := s.dir.Ensure("missing/file")
c.Assert(err, IsNil)
_, err = os.Stat(rv1)
c.Check(err, IsNil)
c.Check(rv1, Matches, s.home+".*"+"missing/file")
// just gets it if existing
rv2, err := s.dir.Ensure("missing/file")
c.Assert(err, IsNil)
c.Check(rv2, Equals, rv1)
}
func (s *xdgdFHSuite) TestEnsureFirstFailures(c *C) {
_, err := s.dir.Ensure(strings.Repeat("*", 1<<9) + "/" + strings.Repeat("*", 1<<9))
c.Assert(err, NotNil)
}

7
ext/go-xdg/doc.go Normal file
View File

@ -0,0 +1,7 @@
// (c) 2014 John R. Lenton. See LICENSE.
// xdg implements helpers for you to use the XDG spec in your apps.
//
// For now, that's just the base directory spec,
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
package xdg