2021-12-01 01:08:47 +00:00
package yqlib
import (
"encoding/csv"
"fmt"
"io"
)
type csvEncoder struct {
2022-01-15 00:57:59 +00:00
separator rune
2021-12-01 01:08:47 +00:00
}
2022-01-15 00:57:59 +00:00
func NewCsvEncoder ( separator rune ) Encoder {
2022-08-01 00:28:34 +00:00
return & csvEncoder { separator : separator }
2021-12-01 01:08:47 +00:00
}
2022-01-15 00:57:59 +00:00
func ( e * csvEncoder ) CanHandleAliases ( ) bool {
return false
}
func ( e * csvEncoder ) PrintDocumentSeparator ( writer io . Writer ) error {
return nil
}
func ( e * csvEncoder ) PrintLeadingContent ( writer io . Writer , content string ) error {
return nil
}
2023-10-18 01:11:53 +00:00
func ( e * csvEncoder ) encodeRow ( csvWriter * csv . Writer , contents [ ] * CandidateNode ) error {
2021-12-02 01:11:15 +00:00
stringValues := make ( [ ] string , len ( contents ) )
for i , child := range contents {
2023-10-18 01:11:53 +00:00
if child . Kind != ScalarNode {
2021-12-02 01:11:15 +00:00
return fmt . Errorf ( "csv encoding only works for arrays of scalars (string/numbers/booleans), child[%v] is a %v" , i , child . Tag )
}
stringValues [ i ] = child . Value
}
2022-01-15 00:57:59 +00:00
return csvWriter . Write ( stringValues )
2021-12-02 01:11:15 +00:00
}
2023-10-18 01:11:53 +00:00
func ( e * csvEncoder ) encodeArrays ( csvWriter * csv . Writer , content [ ] * CandidateNode ) error {
2022-08-01 00:28:34 +00:00
for i , child := range content {
2023-10-18 01:11:53 +00:00
if child . Kind != SequenceNode {
2022-08-01 00:28:34 +00:00
return fmt . Errorf ( "csv encoding only works for arrays of scalars (string/numbers/booleans), child[%v] is a %v" , i , child . Tag )
}
err := e . encodeRow ( csvWriter , child . Content )
if err != nil {
return err
}
}
return nil
}
2023-10-18 01:11:53 +00:00
func ( e * csvEncoder ) extractHeader ( child * CandidateNode ) ( [ ] * CandidateNode , error ) {
if child . Kind != MappingNode {
2022-08-01 00:28:34 +00:00
return nil , fmt . Errorf ( "csv object encoding only works for arrays of flat objects (string key => string/numbers/boolean value), child[0] is a %v" , child . Tag )
}
mapKeys := getMapKeys ( child )
return mapKeys . Content , nil
}
2023-10-18 01:11:53 +00:00
func ( e * csvEncoder ) createChildRow ( child * CandidateNode , headers [ ] * CandidateNode ) [ ] * CandidateNode {
childRow := make ( [ ] * CandidateNode , 0 )
2022-08-01 00:28:34 +00:00
for _ , header := range headers {
keyIndex := findKeyInMap ( child , header )
value := createScalarNode ( nil , "" )
if keyIndex != - 1 {
value = child . Content [ keyIndex + 1 ]
}
childRow = append ( childRow , value )
}
return childRow
}
2023-10-18 01:11:53 +00:00
func ( e * csvEncoder ) encodeObjects ( csvWriter * csv . Writer , content [ ] * CandidateNode ) error {
2022-08-01 00:28:34 +00:00
headers , err := e . extractHeader ( content [ 0 ] )
if err != nil {
return nil
}
err = e . encodeRow ( csvWriter , headers )
if err != nil {
return nil
}
for i , child := range content {
2023-10-18 01:11:53 +00:00
if child . Kind != MappingNode {
2022-08-01 00:28:34 +00:00
return fmt . Errorf ( "csv object encoding only works for arrays of flat objects (string key => string/numbers/boolean value), child[%v] is a %v" , i , child . Tag )
}
row := e . createChildRow ( child , headers )
err = e . encodeRow ( csvWriter , row )
if err != nil {
return err
}
}
return nil
}
2023-10-18 01:11:53 +00:00
func ( e * csvEncoder ) Encode ( writer io . Writer , node * CandidateNode ) error {
if node . Kind == ScalarNode {
return writeString ( writer , node . Value + "\n" )
2023-03-27 02:54:24 +00:00
}
2022-01-15 00:57:59 +00:00
csvWriter := csv . NewWriter ( writer )
csvWriter . Comma = e . separator
2021-12-01 01:08:47 +00:00
// node must be a sequence
2023-10-18 01:11:53 +00:00
if node . Kind != SequenceNode {
2021-12-02 01:11:15 +00:00
return fmt . Errorf ( "csv encoding only works for arrays, got: %v" , node . Tag )
} else if len ( node . Content ) == 0 {
return nil
}
2023-10-18 01:11:53 +00:00
if node . Content [ 0 ] . Kind == ScalarNode {
2022-01-15 00:57:59 +00:00
return e . encodeRow ( csvWriter , node . Content )
2021-12-01 01:08:47 +00:00
}
2023-10-18 01:11:53 +00:00
if node . Content [ 0 ] . Kind == MappingNode {
2022-08-01 00:28:34 +00:00
return e . encodeObjects ( csvWriter , node . Content )
2021-12-01 01:08:47 +00:00
}
2022-08-01 00:28:34 +00:00
return e . encodeArrays ( csvWriter , node . Content )
2021-12-01 01:08:47 +00:00
}