-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpath.carp
152 lines (126 loc) · 5.42 KB
/
path.carp
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
(doc Path "is a simple file path library for Carp.
## Installation
```clojure
(load \"[email protected]:carpentry/[email protected]\")
```
### Usage
The `Path` module mostly operates on `String` arguments. It allows you to
split, join, and merge paths and extensions in a lot of different ways. It also
has some functions to work with the `PATH` environment variable.
It assumes either Windows or POSIX-style separators.")
(defmodule Path
(doc absolute? "checks whether a path is absolute.
As such, it is the inverse to [relative?](#relative?).")
(doc separator "is the default separator we use on this OS.")
(doc separators "is the possible separators we could use on this OS.")
(doc search-path-separator "is the separator for the `PATH` environment
variable we use on this OS.")
(private extension-pat)
(hidden extension-pat)
(private sep-string)
(hidden sep-string)
(windows-only
(defn absolute? [p] (Pattern.matches? #"[A-Za-z]:\\" p))
(def separator \\)
(def separators [\/ \\])
(def search-path-separator \;)
(def extension-pat #"\.[^\\/\.]*$")
(def sep-string "\\"))
(posix-only
(defn absolute? [p] (String.starts-with? p "/"))
(def separator \/)
(def separators [\/])
(def search-path-separator \:)
(def extension-pat #"\.[^/\.]*$")
(def sep-string "/"))
(doc relative? "checks whether a path is relative.
As such, it is the inverse to [absolute?](#absolute?).")
(defn relative? [p] (not (absolute? p)))
(doc separator? "checks whether the character `c` is a path separator on this
OS.")
(defn separator? [c] (Array.contains? &separators c))
(doc separator? "checks whether the character `c` is a separator for the
`PATH` environment variable on this OS.")
(defn search-path-separator? [c] (= search-path-separator c))
(doc path-max "defines the maximum path length on this OS.")
(register path-max Int "PATH_MAX")
(private cwd-)
(hidden cwd-)
(register cwd- (Fn [String Int] String) "getcwd")
(doc cwd "returns the current working directory as a `Maybe`. The ways in
which it can fail are OS-dependent, but it should happen relatively rare.")
(defn cwd []
(let [s (String.allocate path-max (Char.from-int 0))
r (cwd- s path-max)]
(if (null? (cstr &r))
(Maybe.Nothing)
(Maybe.Just r))))
(doc </> "joins `before` and `after` using the default path separator.")
(defn </> [before after] (fmt "%s%c%s" before separator after))
(doc split "splits the path `p` into its components.
As such, it is the inverse to [join](#join).")
(defn split [p] (String.split-by p &[separator]))
(doc join "joins the path components `ps` into a path.
As such, it is the inverse to [split](#split).")
(defn join [ps] (String.join sep-string ps))
(doc filename "gets the filename of the path `p` as a `(Maybe String)`.
It will return `Nothing` if an empty string is passed.")
(defn filename [p] (Array.last &(split p)))
(doc basename "gets the basename of the path `p`.")
(defn basename [p]
(let [split (split p)
but-last (Array.prefix &split (dec (Array.length &split)))]
(String.join sep-string &but-last)))
(doc split-extension "splits the path `p` on its extension.
It will return a `(Maybe (Pair String String))`. `Maybe` because there might not
be an extension, and `Pair` because it will return the part before and after
the extension.
Examples on POSIX:
```
(split-extension \"file.txt\")
; => (Maybe.Just (Pair \"file\" \"txt\"))
(split-extension \"file\")
; => (Maybe.Nothing)
(split-extension \"file/file.txt\")
; => (Maybe.Just (Pair \"file/file\" \"txt\"))
(split-extension \"file.txt/veit\")
; => (Maybe.Nothing)
(split-extension \"file.txt/veit.ext\")
; => (Maybe.Just (Pair \"file.txt/veit\" \"ext\"))
(split-extension \"file/path.txt.bob.fred\")
; => (Maybe.Just (Pair \"file/path.txt.bob\" \"fred\"))
```")
(defn split-extension [p]
(let [i (Pattern.find extension-pat p)]
(if (= -1 i)
(Maybe.Nothing)
(Maybe.Just (Pair.init (prefix p i) (suffix p (inc i)))))))
(doc extension "gets the extension of a file as a `Maybe`.")
(defn extension [p]
(Maybe.apply (split-extension p) &(fn [p] @(Pair.b &p))))
(doc has-extension? "cheks whether the path `p` has an extension.")
(defn has-extension? [p] (Maybe.just? &(split-extension p)))
(doc is-extension? "checks whether the path `p` has the extension `ext`.")
(defn is-extension? [p ext] (= &(extension p) &(Maybe.Just @ext)))
(doc drop-extension "drops the extension of a path `p`. Does nothing if there
is none.")
(defn drop-extension [p]
(match (split-extension p)
(Maybe.Nothing) @p
(Maybe.Just pair) @(Pair.a &pair)))
(doc add-extension "adds an extension `ext` to a path `p`.")
(defn add-extension [p ext] (String.concat &[@p @"." @ext]))
(doc replace-extension "replaces the extension of a path `p` with `ext`. Adds
an extension if there previously was none.")
(defn replace-extension [p ext] (add-extension &(drop-extension p) ext))
(doc absolute "makes an absolute path from `p`.")
(defn absolute [p]
(if (absolute? p)
(Maybe.Just @p)
(Maybe.apply (cwd) &(fn [d] (join &[d @p])))))
(doc split-search-path "splits a `PATH` environment variable `p`.")
(defn split-search-path [p] (String.split-by p &[search-path-separator]))
(doc get-search-path "gets the `PATH` environment variable and splits it.")
(defn get-search-path []
(Maybe.apply (IO.getenv "PATH") &(fn [p] (split-search-path &p))))
)