-
Notifications
You must be signed in to change notification settings - Fork 0
/
path.gleam
120 lines (101 loc) · 2.8 KB
/
path.gleam
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
import gleam/list
import gleam/result
import gleam/string
// The order of `segments` is reversed, because adding and removing from
// the front of lists is much faster.
pub opaque type Path {
Path(segments: List(String), kind: PathKind)
}
pub type PathKind {
Absolute
Relative
}
fn normalize_segments_for(kind kind: PathKind) {
fn(segments: List(String), segment: String) -> List(String) {
case segment {
".." -> {
// If the path is relative, and only consists of segments traveling up (".."),
// then we can continue to go up. Otherwise, remove the last segment.
let only_up_from_here =
list.all(segments, fn(segment) { segment == ".." })
case kind, only_up_from_here {
Relative, True -> [segment, ..segments]
_, _ -> result.unwrap(list.rest(segments), [])
}
}
"" | "." ->
// Allow for a single leading "." on relative paths
case kind, segments {
Relative, [] -> ["."]
_, _ -> segments
}
_ -> [segment, ..segments]
}
}
}
pub fn from_string(path_string: String) -> Path {
let kind = case string.starts_with(path_string, "/") {
True -> Absolute
False -> Relative
}
path_string
|> string.split("/")
|> list.fold([], normalize_segments_for(kind))
|> Path(kind)
}
pub fn and_then(self: Path, piece: Path) -> Path {
case piece.kind {
Absolute -> piece
Relative -> {
let segments =
piece.segments
|> list.reverse()
|> list.fold(self.segments, normalize_segments_for(self.kind))
Path(..self, segments: segments)
}
}
}
pub fn and_then_string(self: Path, piece: String) -> Path {
let piece = from_string(piece)
and_then(self, piece)
}
pub fn append(self: Path, piece: Path) -> Path {
let segments =
piece.segments
|> list.fold(self.segments, normalize_segments_for(self.kind))
Path(..self, segments: segments)
}
pub fn append_string(self: Path, piece: String) -> Path {
let segments =
piece
|> string.split("/")
|> list.fold(self.segments, normalize_segments_for(self.kind))
Path(..self, segments: segments)
}
pub fn absolute(self: Path) -> Path {
Path(..self, kind: Absolute)
}
pub fn relative(self: Path) -> Path {
Path(..self, kind: Relative)
}
pub fn absolute_or_resolve_from(self: Path, root: String) -> Path {
case self.kind {
Absolute -> self
Relative ->
from_string(root)
|> append(self)
}
}
pub fn to_string(self: Path) -> String {
let joined_segments =
self.segments
|> list.reverse()
|> string.join("/")
case self.kind {
Absolute -> "/" <> joined_segments
Relative -> joined_segments
}
}
pub fn equals(self: Path, segments: List(String), kind: PathKind) -> Bool {
list.reverse(self.segments) == segments && self.kind == kind
}