Last active
July 16, 2019 10:39
-
-
Save nerg4l/4f28edfbe4fa9e227d4f72c0411a457c to your computer and use it in GitHub Desktop.
Re: faiface - How to use 'try'
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Boris Bateman | |
gender: man | |
os: Windows | |
lang: PHP | |
name: Darin May | |
gender: woman | |
os: OSX | |
lang: JavaScript | |
name: Shea Wilks | |
gender: nonbinary | |
os: Linux | |
lang: Emacs LISP | |
name: Robert Griesemer | |
gender: man | |
os: Linux | |
lang: Go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A solution where error handling is "lifted" | |
package main | |
import ( | |
"bufio" | |
"flag" | |
"fmt" | |
"io" | |
"os" | |
"path/filepath" | |
"strings" | |
) | |
var path = flag.String("file", "", "path to file") | |
type Respondent struct { | |
Name string | |
Gender string | |
OS string | |
Lang string | |
} | |
type LineAwareScanner struct { | |
*bufio.Scanner | |
line uint | |
} | |
func (s *LineAwareScanner) Scan() bool { | |
r := s.Scanner.Scan() | |
if r { | |
s.line++ | |
} | |
return r | |
} | |
func (s *LineAwareScanner) Line() uint { | |
return s.line | |
} | |
type RespondentScanner struct { | |
scanner *LineAwareScanner | |
resp Respondent | |
err error | |
done bool | |
} | |
func (s *RespondentScanner) parseNextLine(name string) string { | |
// do nothing if an error already occured | |
if s.err != nil { | |
return "" | |
} | |
defer func() { | |
if s.err != nil { | |
s.err = fmt.Errorf("line %d: %s", s.scanner.Line(), s.err) | |
} | |
}() | |
// Handle when scan stops before expected | |
if !s.scanner.Scan() { | |
s.err = io.ErrUnexpectedEOF | |
return "" | |
} | |
// Handle when scanner has an error | |
if err := s.scanner.Err(); err != nil { | |
return "" | |
} | |
line := s.scanner.Text() | |
prefix := name + ":" | |
if !strings.HasPrefix(line, prefix) { | |
s.err = fmt.Errorf("scan field '%s': expected prefix %q", name, prefix) | |
return "" | |
} | |
return strings.TrimSpace(strings.TrimPrefix(line, prefix)) | |
} | |
func (s *RespondentScanner) Scan() bool { | |
// Do not scan if done or an error occurred | |
if s.done || s.err != nil { | |
return false | |
} | |
resp := Respondent{ | |
Name: s.parseNextLine("name"), | |
Gender: s.parseNextLine("gender"), | |
OS: s.parseNextLine("os"), | |
Lang: s.parseNextLine("lang"), | |
} | |
if s.Err() != nil { | |
return false | |
} | |
// Scan should be done in next cycle | |
s.done = !s.scanner.Scan() | |
s.resp = resp | |
return true | |
} | |
// Err returns the first non-EOF error that was encountered by the Scanner. | |
func (s *RespondentScanner) Err() error { | |
if s.err != nil { | |
return s.err | |
} | |
return s.scanner.Err() | |
} | |
// Resp returns the created Respondent after Scan | |
func (s *RespondentScanner) Resp() Respondent { | |
return s.resp | |
} | |
func main() { | |
flag.Parse() | |
if len(*path) == 0 { | |
_, _ = fmt.Println("file flag is required") | |
return | |
} | |
p, err := filepath.Abs(*path) | |
if err != nil { | |
panic(err) | |
} | |
f, err := os.Open(p) | |
if err != nil { | |
panic(err) | |
} | |
defer func() { | |
_ = f.Close() | |
}() | |
s := RespondentScanner{ | |
scanner: &LineAwareScanner{ | |
Scanner: bufio.NewScanner(f), | |
}, | |
done: false, | |
} | |
var resps []Respondent | |
for s.Scan() { | |
resps = append(resps, s.Resp()) | |
} | |
if err := s.Err(); err != nil { | |
_, _ = fmt.Println(fmt.Errorf("parse %s: %s", f.Name(), err)) | |
return | |
} | |
_, _ = fmt.Println(resps) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A solution with on the spot error handling | |
package main | |
import ( | |
"bufio" | |
"errors" | |
"flag" | |
"fmt" | |
"io" | |
"os" | |
"path/filepath" | |
"strings" | |
) | |
var path = flag.String("file", "", "path to file") | |
type Respondent struct { | |
Name string | |
Gender string | |
OS string | |
Lang string | |
} | |
func parseField(line string, name string) (value string, err error) { | |
prefix := name + ":" | |
if !strings.HasPrefix(line, prefix) { | |
return "", fmt.Errorf("scan field '%s': expected prefix %q", name, prefix) | |
} | |
return strings.TrimPrefix(line, prefix), nil | |
} | |
func parseRespondents(f io.Reader) (resps []Respondent, err error) { | |
i := 1 | |
defer func() { | |
if err != nil { | |
err = fmt.Errorf("line %d: %s", i, err) | |
} | |
}() | |
resp := Respondent{} | |
s := bufio.NewScanner(f) | |
resetLine := 5 | |
for s.Scan() { | |
line := s.Text() | |
switch i % resetLine { | |
case 0: | |
if s.Text() != "" { | |
err = errors.New("expected empty line") | |
} | |
case 1: | |
resp.Name, err = parseField(line, "name") | |
case 2: | |
resp.Gender, err = parseField(line, "gender") | |
case 3: | |
resp.OS, err = parseField(line, "os") | |
case 4: | |
resp.Lang, err = parseField(line, "lang") | |
default: | |
} | |
if err != nil { | |
return resps, err | |
} | |
if i % resetLine == resetLine - 1 { | |
resps = append(resps, resp) | |
resp = Respondent{} | |
} | |
i++ | |
} | |
if i % 5 != 0 { | |
return nil, io.ErrUnexpectedEOF | |
} | |
return resps, nil | |
} | |
func main() { | |
flag.Parse() | |
if len(*path) == 0 { | |
_, _ = fmt.Println("file flag is required") | |
return | |
} | |
p, err := filepath.Abs(*path) | |
if err != nil { | |
panic(err) | |
} | |
f, err := os.Open(p) | |
if err != nil { | |
panic(err) | |
} | |
defer func() { | |
_ = f.Close() | |
}() | |
resps, err := parseRespondents(f) | |
if err != nil { | |
_, _ = fmt.Println(fmt.Errorf("parse %s: %s", f.Name(), err)) | |
return | |
} | |
_, _ = fmt.Println(resps) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment