casper

package module
v0.0.0-...-1fb6c41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2019 License: MIT Imports: 14 Imported by: 0

README

go-casper

Package casper provides tools for working with the Casper file data format, for the Go programming language.

Typical uses for Casper is for configuration files, for API responses, for logs, for data transmissions, for serialization, etc.

Documention

Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-casper

GoDoc

Casper File Data Format

The Casper (CAscading PropERties) file data format is inspired by the CSS (Cascading Style Sheets) file data format. In that it looks a lot like it. Although there are some differences. For example:

apple {
	color:	red;
	type:	fruit;
}

banana {
	color:	yellow;
	type:	berry;
}

cherry {
	color: purple;
	type:	fruit;
}

But (instead of being used for style sheets, as with CSS) Casper is meant to be used used a human-readble, human-editable, and machine-readable file data format, to be used for serializing hierarchical key-value pairs.

Casper emphasizes software developer user experience (UX).

Typical uses for Casper is for configuration files, for API responses, for logs, for data transmissions, for serialization, etc.

Casper is meant to be a more human friendly alternative to JSON, YAML, XML, etc.

In particular, Casper is meant to be an improvement, in terms of developer user experience (UX), over JSON by:

  • supporting comments,
  • allowing trailing key-value pair deliminators,
  • supporting the merging of multiple files into one, and
  • supporting cascading,
  • allowing for a notation that allows both the key, and value to be sliced out.

Sample Casper

Here is what a simple Casper file might look like:

apple {
	color:	red;
	type:	fruit;
}

banana {
	color:	yellow;
	type:	berry;
}

cherry {
	color: purple;
	type:	fruit;
}

Here is a sample Casper file used as a config file:

database {
	username: "joeblow";
	password: "password123";
}

http {
	port: 8080;

	log {
		dest: stdout;
	}
}

telnet/port: 2323;
telnet/log/dest: stdout;

Sample Casper Variations

There are multiple ways to write the semantically same Casper data.

Here is the same thing written 3 different ways:

http {
	port: 8080;

	log {
		dest: stdout;
	}
}
http {
	port: 8080;
}

http/log {
		dest: stdout;
}
http/port: 8080;

http/log/dest: stdout;

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Encode

func Encode(writer io.Writer, src interface{}) error

Encode encodes ‘src’ as a Casper file data format, and writes it to ‘writer’.

‘src’ can be either any of the following map types:

• map[string]casper.Valuer

• map[string]encoding.TextMarshaler

• map[string]fmt.StringCaster

• map[string]fmt.Stringer

• map[string][]byte

• map[string]string

• map[string]interface{}

And can also be a struct that the appropriate “casper” struct tag.

Example (MapStringBytes)
var data map[string][]byte = map[string][]byte{
	"apple":  []byte("ONE"),
	"banana": []byte("TWO"),
	"cherry": []byte("THREE"),
}

var buffer bytes.Buffer

var writer io.Writer = &buffer

err := casper.Encode(writer, data)
if nil != err {
	fmt.Printf("ERROR: %s\n", err)
	return
}

fmt.Print(buffer.String())
Output:

apple:	ONE;
banana:	TWO;
cherry:	THREE;
Example (MapStringInterface)
var data map[string]interface{} = map[string]interface{}{
	"apple":  "one",
	"banana": "two",
	"cherry": "three",

	"http": map[string]interface{}{
		"port": "8080",
		"log": map[string]interface{}{
			"dest": "stdout",
		},
	},

	"x-notice": Measurement{3, "cm"},
}

var buffer bytes.Buffer

var writer io.Writer = &buffer

err := casper.Encode(writer, data)
if nil != err {
	fmt.Printf("ERROR: %s\n", err)
	return
}

fmt.Print(buffer.String())
Output:

apple:	one;
banana:	two;
cherry:	three;
http {
	log {
		dest:	stdout;
	}
	port:	8080;
}
x-notice:	3cm;
Example (MapStringString)
var data map[string]string = map[string]string{
	"apple":  "one",
	"banana": "two",
	"cherry": "three",
}

var buffer bytes.Buffer

var writer io.Writer = &buffer

err := casper.Encode(writer, data)
if nil != err {
	fmt.Printf("ERROR: %s\n", err)
	return
}

fmt.Print(buffer.String())
Output:

apple:	one;
banana:	two;
cherry:	three;
Example (MapStringStringCaster)
package main

import (
	"github.com/reiver/go-casper"

	"bytes"
	"fmt"
	"io"
	"strings"
)

type Title struct {
	value string
}

func (receiver Title) String() (string, error) {
	var s string = strings.Title(receiver.value)

	return s, nil
}

func main() {

	var data map[string]casper.StringCaster = map[string]casper.StringCaster{
		"apple":  Title{"one"},
		"banana": Title{"two"},
		"cherry": Title{"three"},
	}

	var buffer bytes.Buffer

	var writer io.Writer = &buffer

	err := casper.Encode(writer, data)
	if nil != err {
		fmt.Printf("ERROR: %s\n", err)
		return
	}

	fmt.Print(buffer.String())

}
Output:

apple:	One;
banana:	Two;
cherry:	Three;
Example (MapStringStringer)
package main

import (
	"github.com/reiver/go-casper"

	"bytes"
	"fmt"
	"io"
)

type Optional struct {
	loaded bool
	value  string
}

func (receiver Optional) String() string {
	if !receiver.loaded {
		return "Nothing()"
	}

	return fmt.Sprintf("Something(%q)", receiver.value)
}

func Nothing() Optional {
	return Optional{}
}

func Something(v string) Optional {
	return Optional{
		loaded: true,
		value:  v,
	}
}

func main() {

	var data map[string]fmt.Stringer = map[string]fmt.Stringer{
		"apple":  Something("red"),
		"banana": Something("yellow"),
		"cherry": Nothing(),
	}

	var buffer bytes.Buffer

	var writer io.Writer = &buffer

	err := casper.Encode(writer, data)
	if nil != err {
		fmt.Printf("ERROR: %s\n", err)
		return
	}

	fmt.Print(buffer.String())

}
Output:

apple:	Something("red");
banana:	Something("yellow");
cherry:	Nothing();
Example (MapStringTextMarshaler)
package main

import (
	"github.com/reiver/go-casper"

	"bytes"
	"encoding"
	"fmt"
	"io"
	"strings"
)

type Upper struct {
	value string
}

func (receiver Upper) MarshalText() ([]byte, error) {
	var s string = strings.ToUpper(receiver.value)

	return []byte(s), nil
}

func main() {

	var data map[string]encoding.TextMarshaler = map[string]encoding.TextMarshaler{
		"apple":  Upper{"one"},
		"banana": Upper{"two"},
		"cherry": Upper{"three"},
	}

	var buffer bytes.Buffer

	var writer io.Writer = &buffer

	err := casper.Encode(writer, data)
	if nil != err {
		fmt.Printf("ERROR: %s\n", err)
		return
	}

	fmt.Print(buffer.String())

}
Output:

apple:	ONE;
banana:	TWO;
cherry:	THREE;
Example (MapStringValuer)
package main

import (
	"github.com/reiver/go-casper"

	"bytes"
	"fmt"
	"io"
)

type Measurement struct {
	value int
	unit  string
}

func (receiver Measurement) CASPERValue() (string, error) {
	var buffer bytes.Buffer

	_, err := fmt.Fprintf(&buffer, "%d%s", receiver.value, receiver.unit)
	if nil != err {
		return "", err
	}

	return buffer.String(), nil
}

func main() {

	var data map[string]casper.Valuer = map[string]casper.Valuer{
		"apple":  Measurement{5, "cm"},
		"banana": Measurement{7, "in"},
		"cherry": Measurement{12, "lbs"},
	}

	var buffer bytes.Buffer

	var writer io.Writer = &buffer

	err := casper.Encode(writer, data)
	if nil != err {
		fmt.Printf("ERROR: %s\n", err)
		return
	}

	fmt.Print(buffer.String())

}
Output:

apple:	5cm;
banana:	7in;
cherry:	12lbs;

Types

type ShortWrite

type ShortWrite interface {
	error

	ShortWrite()

	// Returns what trying to be written.
	Source() string

	// Returns the number of bytes that were expected to be written.
	Expected() int64

	// Returns the number of bytes that were actually written.
	Actual() int64
}

ShortWrite is the error returned from casper.Encode() when, for whatever reason, not everything was able to be written to an io.Writer.

For example:

err := casper.Encode(writer, source)

if nil != err {
	switch err.(type) {
	case casper.ShortWrite:
		//@TODO
	default:
		//@TODO
	}
}

type StringCaster

type StringCaster interface {
	String() (string, error)
}

type UnsupportedSource

type UnsupportedSource interface {
	error

	UnsupportedSource()

	Debug() string
	Source() interface{}
}

UnsupportedSource is the error returned from casper.Encode(), and casper.Value.Scan() when the type of the source is not supported.

For example:

var source int64 // <---- Note that the type is int64, which casper.Encode() does not support (by itself). So it will return an error.

// ...

err := casper.Encode(writer, source)

if nil != err {
	switch err.(type) {
	case casper.UnsupportedSource:
		//@TODO
	default:
		//@TODO
	}
}

Also, for example:

var source int64 // <---- Note that the type is int64, which casper.Encode() does not support (by itself). So it will return an error.

// ...

var value casper.Value

// ...

err := value.Scan(source)

if nil != err {
	switch err.(type) {
	case casper.UnsupportedSource:
		//@TODO
	default:
		//@TODO
	}
}

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value represents a CAPSER value, in the key-value sense of the word “value”.

It is NOT required for this casper.Value type be used to use the tools in this package.

This package works fine without this casper.Value.

casper.Value is provided as a convenience for those looking for a string ‘option type’, that also works seemlessly with the built-in "database/sql", "encoding", "fmt", and "encoding/json" packages.

func NoValue

func NoValue() Value

NoValue creates a casper.Value with no value.

You can use this in if-statements, and switch-statements too.

Example

Here is an example of using casper.NoValue() with an assignment:

var value casper.Value = casper.NoValue()

Example

Here is an example of using casper.NoValue() with an if-statement:

var value casper.Value

// ...

if casper.NoValue() == value {
	//@TODO
}

Example

Here is an example of using casper.NoValue() with an switch-statement:

var value casper.Value

// ...

switch value {
case casper.NoValue():
	//@TODO
default:
	//@TODO
}

func SomeValue

func SomeValue(value string) Value

SomeValue creates a casper.Value with a specific value.

You can use this in if-statements, and switch-statements too.

Example

Here is an example of using casper.SomeValue() with an assignment:

var value casper.Value = casper.SomeValue("Hello world!")

Example

Here is an example of using casper.SomeValue() with an if-statement:

var value casper.Value

// ...

if casper.SomeValue("Hello world!") == value {
	//@TODO
}

Example

Here is an example of using casper.SomeValue() with an switch-statement:

var value casper.Value

// ...

switch value {
case casper.SomeValue("Hello world!"):
	//@TODO
default:
	//@TODO
}

func (Value) CASPERValue

func (receiver Value) CASPERValue() (string, error)

func (Value) Else

func (receiver Value) Else(datum string) Value

func (Value) ElseUnwrap

func (receiver Value) ElseUnwrap(datum string) string

func (Value) GoString

func (receiver Value) GoString() string

GoString makes casper.Value fit the fmt.GoStringer interface.

It gets used with the %#v verb with the printing family of functions in the Go built-in "fmt" package.

I.e., it gets used with: fmt.Fprint(), fmt.Fprintf(), fmt.Fprintln(), fmt.Print(), fmt.Printf(), fmt.Println(), fmt.Sprint(), fmt.Sprintf(), fmt.Sprintln().

Example

var value casper.Value

// ...

fmt.Printf("value = %#v\n", value) // <---- value.GoString() is called by fmt.Printf()

func (Value) Map

func (receiver Value) Map(fn func(string) string) Value

func (*Value) Scan

func (receiver *Value) Scan(src interface{}) error

Scan makes casper.Value fit the database/sql.Scanner interface.

func (Value) String

func (receiver Value) String() (string, error)

func (Value) Then

func (receiver Value) Then(fn func(string) Value) Value

func (*Value) UnmarshalText

func (receiver *Value) UnmarshalText(text []byte) error

Scan makes casper.Value fit the encoding.TextUnmarshaler interface.

func (Value) Unwrap

func (receiver Value) Unwrap() (string, bool)

Unwrap returns the value inside of Value, unless there is nothing inside.

Example

var value casper.Value

// ...

str, loaded := value.Unwrap()
if !loaded {
	//@TODO
}

func (Value) Value

func (receiver Value) Value() (driver.Value, error)

Value makes casper.Value fit the database/sql/driver.Valuer interface.

type Valuer

type Valuer interface {
	CASPERValue() (string, error)
}

If a type has fits the casper.Valuer interface, then casper.Encode() func will use its .CASPERValue() method.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL