1package workspace
2
3import (
4	"os"
5	"path/filepath"
6)
7
8// PathType is either a path to a dir or file, or the name of an exercise.
9type PathType int
10
11const (
12	// TypeExerciseID is the name of an exercise.
13	TypeExerciseID PathType = iota
14	// TypeDir is a relative or absolute path to a directory.
15	TypeDir
16	// TypeFile is a relative or absolute path to a file.
17	TypeFile
18)
19
20// DetectPathType determines whether the given path is a directory, a file, or the name of an exercise.
21func DetectPathType(path string) (PathType, error) {
22	// If it's not an absolute path, make it one.
23	if !filepath.IsAbs(path) {
24		var err error
25		path, err = filepath.Abs(path)
26		if err != nil {
27			return -1, err
28		}
29	}
30
31	// If it doesn't exist, then it's an exercise name.
32	// We'll have to walk the workspace to find it.
33	if _, err := os.Stat(path); err != nil {
34		return TypeExerciseID, nil
35	}
36
37	// We found it. It's an actual path of some sort.
38	info, err := os.Lstat(path)
39	if err != nil {
40		return -1, err
41	}
42
43	// If it's a symlink, resolve it.
44	if info.Mode()&os.ModeSymlink == os.ModeSymlink {
45		src, err := filepath.EvalSymlinks(path)
46		if err != nil {
47			return -1, err
48		}
49		path = src
50		// Overwrite the symlinked info with the source info.
51		info, err = os.Lstat(path)
52		if err != nil {
53			return -1, err
54		}
55	}
56
57	if info.IsDir() {
58		return TypeDir, nil
59	}
60	return TypeFile, nil
61}
62