forked from fsprojects/FAKE
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStringHelper.fs
More file actions
345 lines (272 loc) · 12.8 KB
/
Copy pathStringHelper.fs
File metadata and controls
345 lines (272 loc) · 12.8 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
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
[<AutoOpen>]
/// Contains basic functions for string manipulation.
module Fake.StringHelper
open System
open System.IO
open System.Text
open System.Collections.Generic
/// [omit]
let productName() = "FAKE"
/// Returns if the string is null or empty
let inline isNullOrEmpty value = String.IsNullOrEmpty value
/// Returns if the string is not null or empty
let inline isNotNullOrEmpty value = String.IsNullOrEmpty value |> not
/// Returns if the string is null or empty or completely whitespace
let inline isNullOrWhiteSpace value = isNullOrEmpty value || value |> Seq.forall Char.IsWhiteSpace
/// Replaces the given pattern in the given text with the replacement
let inline replace (pattern : string) replacement (text : string) = text.Replace(pattern, replacement)
/// Converts a sequence of strings to a string with delimiters
let inline separated delimiter (items : string seq) = String.Join(delimiter, Array.ofSeq items)
/// Removes the slashes from the end of the given string
let inline trimSlash (s : string) = s.TrimEnd('\\')
/// Splits the given string at the given char delimiter
let inline split (delimiter : char) (text : string) = text.Split [| delimiter |] |> Array.toList
/// Splits the given string at the given string delimiter
let inline splitStr (delimiterStr : string) (text : string) =
text.Split([| delimiterStr |], StringSplitOptions.None) |> Array.toList
/// Converts a sequence of strings into a string separated with line ends
let inline toLines text = separated Environment.NewLine text
/// Checks whether the given text starts with the given prefix
let startsWith prefix (text : string) = text.StartsWith prefix
/// Checks whether the given text ends with the given suffix
let endsWith suffix (text : string) = text.EndsWith suffix
/// Determines whether the last character of the given <see cref="string" />
/// matches Path.DirectorySeparatorChar.
let endsWithSlash = endsWith (Path.DirectorySeparatorChar.ToString())
/// Replaces the first occurrence of the pattern with the given replacement.
let replaceFirst (pattern : string) replacement (text : string) =
let pos = text.IndexOf pattern
if pos < 0 then text
else text.Remove(pos, pattern.Length).Insert(pos, replacement)
/// Appends a text to a StringBuilder.
let inline append text (builder : StringBuilder) = builder.Append(sprintf "\"%s\" " text)
/// Appends a text to a StringBuilder without surrounding quotes.
let inline appendWithoutQuotes (text : string) (builder : StringBuilder) = builder.Append(sprintf "%s " text)
/// Appends string of function value if option has some value
let inline appendIfSome o f builder =
match o with
| Some(value) -> appendWithoutQuotes (f value) builder
| None -> builder
/// Appends a text if the predicate is true.
let inline appendIfTrue p s builder =
if p then append s builder
else builder
let inline appendIfTrueWithoutQuotes p s builder =
if p then appendWithoutQuotes s builder
else builder
/// Appends a text if the predicate is false.
let inline appendIfFalse p = appendIfTrue (not p)
/// Appends a text if the value is not null.
let inline appendIfNotNull (value : Object) s =
appendIfTrue (value <> null) (match value with
| :? String as sv -> (sprintf "%s%s" s sv)
| _ -> (sprintf "%s%A" s value))
/// Appends a quoted text if the value is not null.
let inline appendQuotedIfNotNull (value : Object) s (builder : StringBuilder) =
if (value = null) then builder
else
(match value with
| :? String as sv -> builder.Append(sprintf "%s\"%s\" " s sv)
| _ -> builder.Append(sprintf "%s\"%A\" " s value))
/// Appends a text if the value is not null.
let inline appendStringIfValueIsNotNull value = appendIfTrue (value <> null)
/// Appends a text if the value is not null or empty.
let inline appendStringIfValueIsNotNullOrEmpty value = appendIfTrue (isNullOrEmpty value |> not)
/// Appends a text if the value is not null or empty.
let inline appendIfNotNullOrEmpty value s = appendIfTrue (isNotNullOrEmpty value) (sprintf "%s%s" s value)
/// Appends all notnull fileNames.
let inline appendFileNamesIfNotNull fileNames (builder : StringBuilder) =
fileNames |> Seq.fold (fun builder file -> appendIfTrue (isNullOrEmpty file |> not) file builder) builder
/// Returns the text from the StringBuilder
let inline toText (builder : StringBuilder) = builder.ToString()
/// [omit]
let private regexes = new Dictionary<_, _>()
/// [omit]
let getRegEx pattern =
match regexes.TryGetValue pattern with
| true, regex -> regex
| _ -> (new System.Text.RegularExpressions.Regex(pattern))
/// [omit]
let regex_replace pattern (replacement : string) text = (getRegEx pattern).Replace(text, replacement)
/// Checks whether the given char is a german umlaut.
let isUmlaut c = Seq.exists ((=) c) [ 'ä'; 'ö'; 'ü'; 'Ä'; 'Ö'; 'Ü'; 'ß' ]
/// Converts all characters in a string to lower case.
let inline toLower (s : string) = s.ToLower()
/// Returns all standard chars and digits.
let charsAndDigits = [ 'a'..'z' ] @ [ 'A'..'Z' ] @ [ '0'..'9' ]
/// Checks whether the given char is a standard char or digit.
let isLetterOrDigit c = List.exists ((=) c) charsAndDigits
/// Trims the given string with the DirectorySeparatorChar
let inline trimSeparator (s : string) = s.TrimEnd Path.DirectorySeparatorChar
/// Trims all special characters from a string.
let inline trimSpecialChars (text : string) =
text
|> Seq.filter isLetterOrDigit
|> Seq.filter (isUmlaut >> not)
|> Seq.fold (fun (acc : string) c -> acc + string c) ""
/// Trims the given string
let inline trim (x : string) =
if isNullOrEmpty x then x
else x.Trim()
/// Trims the given string
let inline trimChars chars (x : string) =
if isNullOrEmpty x then x
else x.Trim chars
/// Trims the start of the given string
let inline trimStartChars chars (x : string) =
if isNullOrEmpty x then x
else x.TrimStart chars
/// Trims the end of the given string
let inline trimEndChars chars (x : string) =
if isNullOrEmpty x then x
else x.TrimEnd chars
/// Lifts a string to an option
let liftString x =
if isNullOrEmpty x then None
else Some x
/// Reads a file line by line
let ReadFile(file : string) =
seq {
use textReader = new StreamReader(file, encoding)
while not textReader.EndOfStream do
yield textReader.ReadLine()
}
/// Reads the first line of a file. This can be helpful to read a password from file.
let ReadLine(file : string) =
use sr = new StreamReader(file, Encoding.Default)
sr.ReadLine()
/// Writes a file line by line
let WriteToFile append fileName (lines : seq<string>) =
let fi = fileInfo fileName
use writer = new StreamWriter(fileName, append && fi.Exists, encoding)
lines |> Seq.iter writer.WriteLine
/// Removes all trailing .0 from a version string
let rec NormalizeVersion(version : string) =
if version = null then "" else
let elements = version.Split [| '.' |]
let mutable version = ""
for i in 0..3 do
if i < elements.Length then
if version = "" then version <- elements.[i]
else version <- version + "." + elements.[i]
if version.EndsWith ".0" then version.Remove(version.Length - 2, 2) |> NormalizeVersion
else version
/// Writes a byte array to a file
let WriteBytesToFile file bytes = File.WriteAllBytes(file, bytes)
/// Writes a string to a file
let WriteStringToFile append fileName (text : string) =
let fi = fileInfo fileName
use writer = new StreamWriter(fileName, append && fi.Exists, encoding)
writer.Write text
/// Replaces the file with the given string
let ReplaceFile fileName text =
let fi = fileInfo fileName
if fi.Exists then
fi.IsReadOnly <- false
fi.Delete()
WriteStringToFile false fileName text
let Colon = ','
/// Writes a file line by line
let WriteFile file lines = WriteToFile false file lines
/// Appends all lines to a file line by line
let AppendToFile file lines = WriteToFile true file lines
/// Reads a file as one text
let inline ReadFileAsString file = File.ReadAllText(file, encoding)
/// Reads a file as one array of bytes
let ReadFileAsBytes file = File.ReadAllBytes file
/// Replaces any occurence of the currentDirectory with .
let inline shortenCurrentDirectory value = replace currentDirectory "." value
/// Checks whether the given text starts with the given prefix
let inline (<*) prefix text = startsWith prefix text
/// Replaces the text in the given file
let ReplaceInFile replaceF fileName =
fileName
|> ReadFileAsString
|> replaceF
|> ReplaceFile fileName
/// Represents Linux line breaks
let LinuxLineBreaks = "\n"
/// Represents Windows line breaks
let WindowsLineBreaks = "\r\n"
/// Represents Mac line breaks
let MacLineBreaks = "\r"
/// Converts all line breaks in a text to windows line breaks
let ConvertTextToWindowsLineBreaks text =
text
|> replace WindowsLineBreaks LinuxLineBreaks
|> replace MacLineBreaks LinuxLineBreaks
|> replace LinuxLineBreaks WindowsLineBreaks
/// Reads a file line by line and replaces all line breaks to windows line breaks
/// - uses a temp file to store the contents in order to prevent OutOfMemory exceptions
let ConvertFileToWindowsLineBreaks(fileName : string) =
use reader = new StreamReader(fileName, encoding)
let tempFileName = Path.GetTempFileName()
use writer = new StreamWriter(tempFileName, false, encoding)
while not reader.EndOfStream do
reader.ReadLine()
|> ConvertTextToWindowsLineBreaks
|> writer.WriteLine
reader.Close()
writer.Close()
File.Delete(fileName)
File.Move(tempFileName, fileName)
/// Removes linebreaks from the given string
let inline RemoveLineBreaks text =
text
|> replace "\r" String.Empty
|> replace "\n" String.Empty
/// Encapsulates the Apostrophe
let inline EncapsulateApostrophe text = replace "'" "`" text
/// A cache of relative path names.
/// [omit]
let relativePaths = new Dictionary<_, _>()
/// <summary>Produces relative path when possible to go from baseLocation to targetLocation.</summary>
/// <param name="baseLocation">The root folder</param>
/// <param name="targetLocation">The target folder</param>
/// <returns>The relative path relative to baseLocation</returns>
/// <exception cref="ArgumentNullException">base or target locations are null or empty</exception>
let ProduceRelativePath baseLocation targetLocation =
if isNullOrEmpty baseLocation then raise (new ArgumentNullException "baseLocation")
if isNullOrEmpty targetLocation then raise (new ArgumentNullException "targetLocation")
if not <| Path.IsPathRooted baseLocation then baseLocation
else if not <| Path.IsPathRooted targetLocation then targetLocation
else if String.Compare(Path.GetPathRoot baseLocation, Path.GetPathRoot targetLocation, true) <> 0 then
targetLocation
else if String.Compare(baseLocation, targetLocation, true) = 0 then "."
else
let resultPath = ref "."
let targetLocation =
if targetLocation |> endsWith directorySeparator then targetLocation
else targetLocation + directorySeparator
let baseLocation =
if baseLocation |> endsWith directorySeparator then ref (baseLocation.Substring(0, baseLocation.Length - 1))
else ref baseLocation
while not <| targetLocation.StartsWith(!baseLocation + directorySeparator, StringComparison.OrdinalIgnoreCase) do
resultPath := !resultPath + directorySeparator + ".."
baseLocation := Path.GetDirectoryName !baseLocation
if (!baseLocation) |> endsWith directorySeparator then
baseLocation := (!baseLocation).Substring(0, (!baseLocation).Length - 1)
resultPath
:= (!resultPath + targetLocation.Substring((!baseLocation).Length))
|> replace (directorySeparator + directorySeparator) directorySeparator
// preprocess .\..\ case
if (sprintf ".%s..%s" directorySeparator directorySeparator) <* (!resultPath) then
(!resultPath).Substring(2, (!resultPath).Length - 3)
else (!resultPath).Substring(0, (!resultPath).Length - 1)
/// Replaces the absolute path to a relative path.
let inline toRelativePath value =
match relativePaths.TryGetValue value with
| true, x -> x
| _ ->
let x = ProduceRelativePath currentDirectory value
relativePaths.Add(value, x)
x
/// Find a regex pattern in a text and replaces it with the given replacement.
let (>=>) pattern replacement text = regex_replace pattern replacement text
/// Determines if a text matches a given regex pattern.
let (>**) pattern text = (getRegEx pattern).IsMatch text
/// Decodes a Base64-encoded UTF-8-encoded string
let DecodeBase64Utf8String(text : string) =
text
|> Convert.FromBase64String
|> Encoding.UTF8.GetString