package format import ( "encoding/json" "fmt" "path/filepath" "strings" "gopkg.in/yaml.v3" ) type DataFormat string const ( List DataFormat = "list" JSON DataFormat = "json" YAML DataFormat = "yaml" ) func (df DataFormat) String() string { return string(df) } func (df *DataFormat) Set(v string) error { switch DataFormat(v) { case List, JSON, YAML: *df = DataFormat(v) return nil default: return fmt.Errorf("must be one of %v", []DataFormat{ List, JSON, YAML, }) } } func (df DataFormat) Type() string { return "DataFormat" } // Marshal marshals arbitrary data into a byte slice formatted as outFormat. // If a marshalling error occurs or outFormat is unknown, an error is returned. // // Supported values are: json, list, yaml func Marshal(data any, outFormat DataFormat) ([]byte, error) { switch outFormat { case JSON: if bytes, err := json.MarshalIndent(data, "", " "); err != nil { return nil, fmt.Errorf("failed to marshal data into JSON: %w", err) } else { return bytes, nil } case YAML: if bytes, err := yaml.Marshal(data); err != nil { return nil, fmt.Errorf("failed to marshal data into YAML: %w", err) } else { return bytes, nil } case List: return nil, fmt.Errorf("this data format cannot be marshaled") default: return nil, fmt.Errorf("unknown data format: %s", outFormat) } } // Unmarshal unmarshals a byte slice formatted as inFormat into an interface // v. If an unmarshalling error occurs or inFormat is unknown, an error is // returned. // // Supported values are: json, list, yaml func Unmarshal(data []byte, v any, inFormat DataFormat) error { switch inFormat { case JSON: if err := json.Unmarshal(data, v); err != nil { return fmt.Errorf("failed to unmarshal data into JSON: %w", err) } case YAML: if err := yaml.Unmarshal(data, v); err != nil { return fmt.Errorf("failed to unmarshal data into YAML: %w", err) } case List: return fmt.Errorf("this data format cannot be unmarshaled") default: return fmt.Errorf("unknown data format: %s", inFormat) } return nil } // DataFormatFromFileExt determines the type of the contents // (JSON or YAML) based on the filname extension. The default // format is passed in, so if it doesn't match one of the cases, // that's what we will use. The defaultFmt value takes into account // both the standard default format (JSON) and any command line // change to that provided by options. func DataFormatFromFileExt(path string, defaultFmt DataFormat) DataFormat { switch strings.TrimLeft(strings.ToLower(filepath.Ext(path)), ".") { case JSON.String(): // The file is a JSON file return JSON case YAML.String(), "yml": // The file is a YAML file return YAML } return defaultFmt }