-
-
Notifications
You must be signed in to change notification settings - Fork 369
Expand file tree
/
Copy pathworkspace.go
More file actions
121 lines (105 loc) · 2.87 KB
/
workspace.go
File metadata and controls
121 lines (105 loc) · 2.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package workspace
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var errMissingMetadata = errors.New("no exercise metadata file found")
// IsMissingMetadata verifies the type of error.
func IsMissingMetadata(err error) bool {
return err == errMissingMetadata
}
// Workspace represents a user's Exercism workspace.
// It may contain a user's own exercises, and other people's
// exercises that they've downloaded to look at or run locally.
type Workspace struct {
Dir string
}
// New returns a configured workspace.
func New(dir string) (Workspace, error) {
_, err := os.Lstat(dir)
if err != nil {
return Workspace{}, err
}
dir, err = filepath.EvalSymlinks(dir)
if err != nil {
return Workspace{}, err
}
return Workspace{Dir: dir}, nil
}
// PotentialExercises are a first-level guess at the user's exercises.
// It looks at the workspace structurally, and guesses based on
// the location of the directory. E.g. any top level directory
// within the workspace (except 'users') is assumed to be a
// track, and any directory within there again is assumed to
// be an exercise.
func (ws Workspace) PotentialExercises() ([]Exercise, error) {
exercises := []Exercise{}
topInfos, err := ioutil.ReadDir(ws.Dir)
if err != nil {
return nil, err
}
for _, topInfo := range topInfos {
if !topInfo.IsDir() {
continue
}
if topInfo.Name() == "users" {
continue
}
subInfos, err := ioutil.ReadDir(filepath.Join(ws.Dir, topInfo.Name()))
if err != nil {
return nil, err
}
for _, subInfo := range subInfos {
if !subInfo.IsDir() {
continue
}
exercises = append(exercises, Exercise{Track: topInfo.Name(), Slug: subInfo.Name(), Root: ws.Dir})
}
}
return exercises, nil
}
// Exercises returns the user's exercises within the workspace.
// This doesn't find legacy exercises where the metadata is missing.
func (ws Workspace) Exercises() ([]Exercise, error) {
candidates, err := ws.PotentialExercises()
if err != nil {
return nil, err
}
exercises := make([]Exercise, 0, len(candidates))
for _, candidate := range candidates {
ok, err := candidate.HasMetadata()
if err != nil {
return nil, err
}
if ok {
exercises = append(exercises, candidate)
}
}
return exercises, nil
}
// ExerciseDir determines the root directory of an exercise.
// This is the directory that contains the exercise metadata file.
func (ws Workspace) ExerciseDir(s string) (string, error) {
if !strings.HasPrefix(s, ws.Dir) {
return "", errors.New("not in workspace")
}
path := s
for {
if path == ws.Dir {
return "", errMissingMetadata
}
if _, err := os.Lstat(path); os.IsNotExist(err) {
return "", err
}
if _, err := os.Lstat(filepath.Join(path, metadataFilepath)); err == nil {
return path, nil
}
if _, err := os.Lstat(filepath.Join(path, legacyMetadataFilename)); err == nil {
return path, nil
}
path = filepath.Dir(path)
}
}