-
Notifications
You must be signed in to change notification settings - Fork 2
Refactoring Outputs to implement a standard interface #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package output | ||
|
|
||
| import ( | ||
| "fmt" | ||
| ) | ||
|
|
||
| type InvalidOutput struct { | ||
| Config Config | ||
| } | ||
|
|
||
| func (o *InvalidOutput) GenerateOutput(elements interface{}) (string, error) { | ||
| return "", fmt.Errorf("Unrecognized output format: %s", o.Config.Format) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package output | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| ) | ||
|
|
||
| type JSONOutput struct { | ||
| Config Config | ||
| } | ||
|
|
||
| func (o *JSONOutput) GenerateOutput(elements interface{}) (string, error) { | ||
| if _, ok := elements.([]interface{}); ok { | ||
| if j, err := json.MarshalIndent(elements, " ", " "); err != nil { | ||
| return "", err | ||
| } else { | ||
| return string(j), nil | ||
| } | ||
| } else { | ||
| if _, ok := elements.(map[string]interface{}); ok { | ||
| if j, err := json.MarshalIndent(elements, " ", " "); err != nil { | ||
| return "", err | ||
| } else { | ||
| return string(j), nil | ||
| } | ||
| } else { | ||
| return fmt.Sprintf("%s", elements), nil | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,8 @@ | ||
| package output | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "sort" | ||
| "strings" | ||
| ) | ||
| type OutputBuilder interface { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, so we are declaring a public interface here... This is intended to communicate to implementors of new output types that their output struct, say
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe implementations have to match exactly the same number of methods as well as exact input and output types. In this case, if a struct would implement anything other than GenerateOutput, it would no longer conform to the interface.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think 'struct typing' allows any struct that minimally implements the interface, but may implement methods outside the interface, to be used as the interface. For example: https://play.golang.org/p/2JS1qC9kca
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah - I did not know that :) |
||
| GenerateOutput(interface{}) (string, error) | ||
| } | ||
|
|
||
| type Config struct { | ||
| Format string | ||
|
|
@@ -20,111 +17,21 @@ func (o *Output) Generate(elements interface{}) (string, error) { | |
| return "", nil | ||
| } | ||
|
|
||
| var v OutputBuilder | ||
| switch o.Config.Format { | ||
| case "json": | ||
| return o.JSONOutput(elements) | ||
| case "shell": | ||
| return o.ShellOutput(elements) | ||
| } | ||
|
|
||
| return "", fmt.Errorf("Unrecognized output format: %s", o.Config.Format) | ||
| } | ||
|
|
||
| func (o *Output) JSONOutput(elements interface{}) (string, error) { | ||
| if _, ok := elements.([]interface{}); ok { | ||
| if j, err := json.MarshalIndent(elements, " ", " "); err != nil { | ||
| return "", err | ||
| } else { | ||
| return string(j), nil | ||
| } | ||
| } else { | ||
| if _, ok := elements.(map[string]interface{}); ok { | ||
| if j, err := json.MarshalIndent(elements, " ", " "); err != nil { | ||
| return "", err | ||
| } else { | ||
| return string(j), nil | ||
| } | ||
| } else { | ||
| return fmt.Sprintf("%s", elements), nil | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func (o *Output) ShellOutput(elements interface{}) (string, error) { | ||
| var keys []string | ||
| var output string | ||
|
|
||
| parsed := make(map[string]string) | ||
| _, _, parsed = parseElementsForShell("elements", elements, parsed) | ||
|
|
||
| for k := range parsed { | ||
| keys = append(keys, k) | ||
| } | ||
|
|
||
| sort.Strings(keys) | ||
|
|
||
| for _, k := range keys { | ||
| output = fmt.Sprintf("%s%s=\"%s\"\n", output, k, parsed[k]) | ||
| } | ||
|
|
||
| return output, nil | ||
| } | ||
|
|
||
| func parseElementsForShell(label string, elements interface{}, parsed map[string]string) (string, interface{}, map[string]string) { | ||
| var e interface{} | ||
| switch elements.(type) { | ||
| case map[string]interface{}: | ||
| for key, subElements := range elements.(map[string]interface{}) { | ||
| l := fmt.Sprintf("%s_%s", label, sanitizeKeyForShell(key)) | ||
| _, e, parsed = parseElementsForShell(l, subElements, parsed) | ||
| v = &JSONOutput{ | ||
| Config: o.Config, | ||
| } | ||
| case []interface{}: | ||
| for i, subElements := range elements.([]interface{}) { | ||
| l := fmt.Sprintf("%s_%d", label, i) | ||
| _, e, parsed = parseElementsForShell(l, subElements, parsed) | ||
| case "shell": | ||
| v = &ShellOutput{ | ||
| Config: o.Config, | ||
| } | ||
| default: | ||
| parsed[label] = fmt.Sprintf("%v", elements) | ||
| return label, nil, parsed | ||
| v = &InvalidOutput{ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Safe zero value/Null Object pattern. Makes sense. |
||
| Config: o.Config, | ||
| } | ||
| } | ||
|
|
||
| return label, e, parsed | ||
| } | ||
|
|
||
| func sanitizeKeyForShell(key string) string { | ||
| // replace non-compliant shell characters with underscores, | ||
| // per IEEE Std 1003.1-2001 | ||
| r := strings.NewReplacer( | ||
| "`", "_", | ||
| "~", "_", | ||
| "!", "_", | ||
| "@", "_", | ||
| "#", "_", | ||
| "$", "_", | ||
| "%", "_", | ||
| "^", "_", | ||
| "&", "_", | ||
| "*", "_", | ||
| "(", "_", | ||
| ")", "_", | ||
| "-", "_", | ||
| "+", "_", | ||
| "=", "_", | ||
| "{", "_", | ||
| "}", "_", | ||
| "[", "_", | ||
| "]", "_", | ||
| "|", "_", | ||
| ":", "_", | ||
| ";", "_", | ||
| "'", "_", | ||
| "\"", "_", | ||
| "/", "_", | ||
| "?", "_", | ||
| ",", "_", | ||
| ".", "_", | ||
| "<", "_", | ||
| ">", "_", | ||
| ) | ||
| return r.Replace(key) | ||
| return v.GenerateOutput(elements) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package output | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "sort" | ||
| "strings" | ||
| ) | ||
|
|
||
| type ShellOutput struct { | ||
| Config Config | ||
| } | ||
|
|
||
| func (o *ShellOutput) GenerateOutput(elements interface{}) (string, error) { | ||
| var keys []string | ||
| var output string | ||
|
|
||
| parsed := make(map[string]string) | ||
| _, _, parsed = parseElementsForShell("elements", elements, parsed) | ||
|
|
||
| for k := range parsed { | ||
| keys = append(keys, k) | ||
| } | ||
|
|
||
| sort.Strings(keys) | ||
|
|
||
| for _, k := range keys { | ||
| output = fmt.Sprintf("%s%s=\"%s\"\n", output, k, parsed[k]) | ||
| } | ||
|
|
||
| return output, nil | ||
| } | ||
|
|
||
| func parseElementsForShell(label string, elements interface{}, parsed map[string]string) (string, interface{}, map[string]string) { | ||
| var e interface{} | ||
| switch elements.(type) { | ||
| case map[string]interface{}: | ||
| for key, subElements := range elements.(map[string]interface{}) { | ||
| l := fmt.Sprintf("%s_%s", label, sanitizeKeyForShell(key)) | ||
| _, e, parsed = parseElementsForShell(l, subElements, parsed) | ||
| } | ||
| case []interface{}: | ||
| for i, subElements := range elements.([]interface{}) { | ||
| l := fmt.Sprintf("%s_%d", label, i) | ||
| _, e, parsed = parseElementsForShell(l, subElements, parsed) | ||
| } | ||
| default: | ||
| parsed[label] = fmt.Sprintf("%v", elements) | ||
| return label, nil, parsed | ||
| } | ||
|
|
||
| return label, e, parsed | ||
| } | ||
|
|
||
| func sanitizeKeyForShell(key string) string { | ||
| // replace non-compliant shell characters with underscores, | ||
| // per IEEE Std 1003.1-2001 | ||
| r := strings.NewReplacer( | ||
| "`", "_", | ||
| "~", "_", | ||
| "!", "_", | ||
| "@", "_", | ||
| "#", "_", | ||
| "$", "_", | ||
| "%", "_", | ||
| "^", "_", | ||
| "&", "_", | ||
| "*", "_", | ||
| "(", "_", | ||
| ")", "_", | ||
| "-", "_", | ||
| "+", "_", | ||
| "=", "_", | ||
| "{", "_", | ||
| "}", "_", | ||
| "[", "_", | ||
| "]", "_", | ||
| "|", "_", | ||
| ":", "_", | ||
| ";", "_", | ||
| "'", "_", | ||
| "\"", "_", | ||
| "/", "_", | ||
| "?", "_", | ||
| ",", "_", | ||
| ".", "_", | ||
| "<", "_", | ||
| ">", "_", | ||
| ) | ||
| return r.Replace(key) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, here you're 'struct typing', rather than explicitly declaring an
type Outputer interface, right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct.