2015-09-26 22:15:49 +00:00
package main
import (
2015-09-28 02:00:38 +00:00
"fmt"
"github.com/codegangsta/cli"
"gopkg.in/yaml.v2"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
2015-09-26 22:15:49 +00:00
)
func main ( ) {
2015-09-28 02:00:38 +00:00
app := cli . NewApp ( )
app . Name = "yaml"
app . Usage = "command line tool for reading and writing yaml"
app . Commands = [ ] cli . Command {
{
Name : "read" ,
Aliases : [ ] string { "r" } ,
2015-10-03 08:28:30 +00:00
Usage : "read <filename> <path>\n\te.g.: yaml read sample.yaml a.b.c\n\t(default) reads a property from a given yaml file\n" ,
2015-09-28 01:58:56 +00:00
Action : readProperty ,
2015-09-28 02:00:38 +00:00
} ,
2015-09-29 00:56:28 +00:00
{
Name : "write" ,
Aliases : [ ] string { "w" } ,
2015-10-03 08:28:30 +00:00
Usage : "write <filename> <path> <value>\n\te.g.: yaml write sample.yaml a.b.c 5\n\tupdates a property from a given yaml file, outputs to stdout\n" ,
2015-09-29 00:56:28 +00:00
Action : writeProperty ,
} ,
2015-10-03 08:28:30 +00:00
{
Name : "write-inplace" ,
Aliases : [ ] string { "wi" } ,
Usage : "wi <filename> <path> <value>\n\te.g.: yaml wi sample.yaml a.b.c 5\n\tupdates a property from a given yaml file and saves it to the given filename (sample.yaml)\n" ,
Action : writePropertyInPlace ,
} ,
2015-09-28 02:00:38 +00:00
}
2015-09-28 01:58:56 +00:00
app . Action = readProperty
2015-09-28 02:00:38 +00:00
app . Run ( os . Args )
2015-09-26 23:38:44 +00:00
}
2015-09-28 01:58:56 +00:00
func readProperty ( c * cli . Context ) {
2015-09-29 06:29:32 +00:00
2015-09-28 01:58:56 +00:00
var parsedData map [ interface { } ] interface { }
2015-09-29 06:29:32 +00:00
2015-09-28 01:58:56 +00:00
readYaml ( c , & parsedData )
2015-09-27 03:18:33 +00:00
2015-09-29 06:29:32 +00:00
if len ( c . Args ( ) ) == 1 {
2015-10-03 07:48:54 +00:00
printYaml ( parsedData )
2015-09-29 06:29:32 +00:00
os . Exit ( 0 )
}
2015-10-03 05:10:29 +00:00
var paths = parsePath ( c . Args ( ) [ 1 ] )
2015-09-27 03:18:33 +00:00
2015-10-03 07:48:54 +00:00
printYaml ( readMap ( parsedData , paths [ 0 ] , paths [ 1 : len ( paths ) ] ) )
2015-09-29 00:56:12 +00:00
}
2015-09-29 00:56:28 +00:00
func writeProperty ( c * cli . Context ) {
2015-10-03 08:28:30 +00:00
printYaml ( updateProperty ( c ) )
}
func writePropertyInPlace ( c * cli . Context ) {
updatedYaml := updateProperty ( c )
ioutil . WriteFile ( c . Args ( ) [ 0 ] , [ ] byte ( updatedYaml ) , 0644 )
}
func updateProperty ( c * cli . Context ) string {
2015-09-29 00:56:28 +00:00
var parsedData map [ interface { } ] interface { }
readYaml ( c , & parsedData )
2015-10-01 00:41:43 +00:00
if len ( c . Args ( ) ) < 3 {
2015-09-29 06:29:32 +00:00
log . Fatalf ( "Must provide <filename> <path_to_update> <value>" )
}
2015-10-03 05:10:29 +00:00
var paths = parsePath ( c . Args ( ) [ 1 ] )
2015-09-29 00:56:28 +00:00
2015-10-03 07:25:13 +00:00
write ( parsedData , paths [ 0 ] , paths [ 1 : len ( paths ) ] , getValue ( c . Args ( ) [ 2 ] ) )
2015-09-29 00:56:28 +00:00
2015-10-03 08:28:30 +00:00
return yamlToString ( parsedData )
2015-09-29 00:56:28 +00:00
}
2015-10-03 07:25:13 +00:00
func getValue ( argument string ) interface { } {
2015-10-01 00:41:43 +00:00
var value , err interface { }
2015-10-03 07:25:13 +00:00
var inQuotes = argument [ 0 ] == '"'
if ! inQuotes {
2015-10-01 00:41:43 +00:00
value , err = strconv . ParseFloat ( argument , 64 )
if err == nil {
return value
}
value , err = strconv . ParseBool ( argument )
if err == nil {
return value
}
2015-10-03 07:25:13 +00:00
return argument
2015-10-01 00:41:43 +00:00
}
2015-10-03 07:25:13 +00:00
return argument [ 1 : len ( argument ) - 1 ]
2015-10-01 00:41:43 +00:00
}
2015-10-03 07:48:54 +00:00
func printYaml ( context interface { } ) {
2015-10-03 08:28:30 +00:00
fmt . Println ( yamlToString ( context ) )
}
func yamlToString ( context interface { } ) string {
2015-09-29 01:05:28 +00:00
out , err := yaml . Marshal ( context )
if err != nil {
log . Fatalf ( "error printing yaml: %v" , err )
}
2015-09-29 06:29:32 +00:00
outStr := string ( out )
2015-10-03 07:48:54 +00:00
// trim the trailing new line as it's easier for a script to add
// it in if required than to remove it
2015-10-03 08:28:30 +00:00
return strings . Trim ( outStr , "\n " )
2015-09-27 03:18:33 +00:00
}
2015-09-28 01:58:56 +00:00
func readYaml ( c * cli . Context , parsedData * map [ interface { } ] interface { } ) {
2015-09-28 02:00:38 +00:00
if len ( c . Args ( ) ) == 0 {
log . Fatalf ( "Must provide filename" )
}
2015-09-28 01:58:56 +00:00
var rawData = readFile ( c . Args ( ) [ 0 ] )
2015-09-26 22:20:42 +00:00
2015-09-28 01:58:56 +00:00
err := yaml . Unmarshal ( [ ] byte ( rawData ) , & parsedData )
2015-09-28 02:00:38 +00:00
if err != nil {
log . Fatalf ( "error: %v" , err )
}
2015-09-27 03:18:33 +00:00
}
2015-09-26 22:15:49 +00:00
2015-09-28 01:58:56 +00:00
func readFile ( filename string ) [ ] byte {
var rawData , readError = ioutil . ReadFile ( filename )
if readError != nil {
log . Fatalf ( "error: %v" , readError )
2015-09-28 02:00:38 +00:00
}
2015-09-28 01:58:56 +00:00
return rawData
2015-09-27 03:11:10 +00:00
}