github.com/exercism/configlet@v3.9.3-0.20200318193232-c70be6269e71+incompatible/track/exercise.go (about) 1 package track 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "regexp" 8 "strings" 9 ) 10 11 // Exercise is an implementation of an Exercism exercise. 12 type Exercise struct { 13 Slug string 14 ReadmePath string 15 SolutionPath string 16 TestSuitePath string 17 } 18 19 // NewExercise loads an exercise. 20 func NewExercise(root string, pg PatternGroup) (Exercise, error) { 21 ex := Exercise{ 22 Slug: filepath.Base(root), 23 } 24 25 err := setPath(root, pg.SolutionPattern, &ex.SolutionPath) 26 if err != nil { 27 return ex, err 28 } 29 30 err = setPath(root, pg.TestPattern, &ex.TestSuitePath) 31 if err != nil { 32 return ex, err 33 } 34 35 err = setPath(root, "README\\.md", &ex.ReadmePath) 36 return ex, err 37 } 38 39 // setPath sets the value of field to the file path matched by pattern. 40 // The resulting file path, if matched, will be relative to root. 41 func setPath(root, pattern string, field *string) error { 42 43 if pattern == "" { 44 return nil 45 } 46 47 rgx, err := regexp.Compile(pattern) 48 if err != nil { 49 return err 50 } 51 52 walkFn := func(path string, info os.FileInfo, err error) error { 53 if info.IsDir() { 54 return nil 55 } 56 57 if rgx.Match([]byte(path)) { 58 prefix := fmt.Sprintf("%s%s", root, string(filepath.Separator)) 59 *field = strings.Replace(path, prefix, "", 1) 60 } 61 return nil 62 } 63 64 return filepath.Walk(root, walkFn) 65 } 66 67 // HasReadme checks that an exercise has a README. 68 func (ex Exercise) HasReadme() bool { 69 return ex.ReadmePath != "" 70 } 71 72 // HasTestSuite checks that an exercise has a test suite. 73 func (ex Exercise) HasTestSuite() bool { 74 return ex.TestSuitePath != "" 75 } 76 77 // IsValid checks that an exercise has a sample solution. 78 func (ex Exercise) IsValid() bool { 79 return ex.SolutionPath != "" 80 }