@@ -27,6 +27,11 @@ const (
2727 fUnknown
2828)
2929
30+ const (
31+ defaultFormatFlagValue = "unspecified"
32+ defaultWrapFlagValue = "key"
33+ )
34+
3035// convertMapsToStringMaps recursively converts values of type
3136// map[interface{}]interface{} contained in item to map[string]interface{}. This
3237// is needed before the encoders for TOML and JSON can accept data returned by
@@ -58,7 +63,7 @@ func convertMapsToStringMaps(item interface{}) (res interface{}, err error) {
5863
5964// convertNumberToInt64 recursively walks the structures contained in item
6065// converting values of the type json.Number to int64 or, failing that, float64.
61- // This approach is meant to prevent encoders putting numbers stored as
66+ // This approach is meant to prevent encoders from putting numbers stored as
6267// json.Number in quotes or encoding large intergers in scientific notation.
6368func convertNumberToInt64 (item interface {}) (res interface {}, err error ) {
6469 switch item .(type ) {
@@ -104,7 +109,7 @@ func stringToFormat(s string) (f format, err error) {
104109 return fYAML , nil
105110 case "json" :
106111 return fJSON , nil
107- case "unknown" :
112+ case defaultFormatFlagValue :
108113 return fPlaceholder , errors .New ("placeholder format" )
109114 default :
110115 return fUnknown , errors .New ("cannot convert string to format: '" +
@@ -131,13 +136,10 @@ func filenameToFormat(s string) (inputf format, outputf format, err error) {
131136 return prefix , suffix , nil
132137}
133138
134- // remarshal converts input data of format inputFormat to outputFormat and
135- // returns the result.
136- func remarshal (input []byte , inputFormat format , outputFormat format ,
137- indentJSON bool ) (result []byte , err error ) {
138- var data interface {}
139-
140- // Decode the serialized data.
139+ // unmarshal decodes serialized data in the format inputFormat into a structure
140+ // of nested maps and slices.
141+ func unmarshal (input []byte , inputFormat format ) (data interface {},
142+ err error ) {
141143 switch inputFormat {
142144 case fTOML :
143145 _ , err = toml .Decode (string (input ), & data )
@@ -157,8 +159,13 @@ func remarshal(input []byte, inputFormat format, outputFormat format,
157159 if err != nil {
158160 return nil , err
159161 }
162+ return
163+ }
160164
161- // Reencode the data in the output format.
165+ // marshal encodes data stored in nested maps and slices in the format
166+ // outputFormat.
167+ func marshal (data interface {}, outputFormat format ,
168+ indentJSON bool ) (result []byte , err error ) {
162169 switch outputFormat {
163170 case fTOML :
164171 buf := new (bytes.Buffer )
@@ -177,31 +184,36 @@ func remarshal(input []byte, inputFormat format, outputFormat format,
177184 if err != nil {
178185 return nil , err
179186 }
180-
181187 return
182188}
183189
184- func main () {
185- var inputFile , outputFile , inputFormatStr , outputFormatStr string
186- var inputFormat , outputFormat format
187- indentJSON := true
190+ // processCommandLine parses the command line arguments (including os.Args[0],
191+ // the program name) and sets the input and the output file names as well as
192+ // other conversion options based on them.
193+ func processCommandLine () (inputFile string , outputFile string ,
194+ inputFormat format , outputFormat format ,
195+ indentJSON bool , wrap string , unwrap string ) {
196+ var inputFormatStr , outputFormatStr string
188197
189- // Parse the command line arguments and choose the input and the output
190- // format.
191198 flag .StringVar (& inputFile , "i" , "-" , "input file" )
192199 flag .StringVar (& outputFile , "o" , "-" , "output file" )
200+ flag .StringVar (& wrap , "wrap" , defaultWrapFlagValue ,
201+ "wrap the data in a map type with the given key" )
202+ flag .StringVar (& unwrap , "unwrap" , defaultWrapFlagValue ,
203+ "only output the data stored under the given key" )
193204
194- // See if our executable is named, e.g., "json2yaml".
205+ // See if our program is named, e.g., "json2yaml" (normally due to having
206+ // been started through a symlink).
195207 inputFormat , outputFormat , err := filenameToFormat (os .Args [0 ])
196208 formatFromProgramName := err == nil
197209 if ! formatFromProgramName {
198210 // Only give the user an option to specify the input and the output
199211 // format with flags when it is mandatory, i.e., when we are *not* being
200212 // run as "json2yaml" or similar. This makes the usage messages for the
201213 // "x2y" commands more accurate as well.
202- flag .StringVar (& inputFormatStr , "if" , "unknown" ,
214+ flag .StringVar (& inputFormatStr , "if" , defaultFormatFlagValue ,
203215 "input format ('toml', 'yaml' or 'json')" )
204- flag .StringVar (& outputFormatStr , "of" , "unknown" ,
216+ flag .StringVar (& outputFormatStr , "of" , defaultFormatFlagValue ,
205217 "input format ('toml', 'yaml' or 'json')" )
206218 }
207219 if ! formatFromProgramName || outputFormat == fJSON {
@@ -252,9 +264,16 @@ func main() {
252264 os .Exit (1 )
253265 }
254266 }
267+ return
268+ }
269+
270+ func main () {
271+ inputFile , outputFile , inputFormat , outputFormat ,
272+ indentJSON , wrap , unwrap := processCommandLine ()
255273
256274 // Read the input data from either standard input or a file.
257275 var input []byte
276+ var err error
258277 if inputFile == "" || inputFile == "-" {
259278 input , err = ioutil .ReadAll (os .Stdin )
260279 } else {
@@ -270,11 +289,33 @@ func main() {
270289
271290 }
272291
273- output , err := remarshal (input , inputFormat , outputFormat , indentJSON )
292+ // Convert the input data from inputFormat to outputFormat.
293+ data , err := unmarshal (input , inputFormat )
274294 if err != nil {
275295 fmt .Println (err )
276296 os .Exit (1 )
277297 }
298+ // Unwrap and/or wrap the data in a map if we were told to.
299+ if unwrap != defaultWrapFlagValue {
300+ temp , ok := data .(map [string ]interface {})
301+ if ! ok {
302+ fmt .Printf ("cannot unwrap data: top-level value not a map\n " )
303+ os .Exit (1 )
304+ }
305+ data , ok = temp [unwrap ]
306+ if ! ok {
307+ fmt .Printf ("cannot unwrap data: no key '%s'\n " , unwrap )
308+ os .Exit (1 )
309+ }
310+ }
311+ if wrap != defaultWrapFlagValue {
312+ data = map [string ]interface {}{wrap : data }
313+ }
314+ output , err := marshal (data , outputFormat , indentJSON )
315+ if err != nil {
316+ fmt .Printf ("cannot convert data: %v\n " , err )
317+ os .Exit (1 )
318+ }
278319
279320 // Print the result to either standard output or a file.
280321 if outputFile == "" || outputFile == "-" {
0 commit comments