Nuke.Cola
Loading...
Searching...
No Matches
PathExtensions.cs
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Linq;
5using System.Threading.Tasks;
6using System.IO;
7using System.Reflection;
8using Nuke.Common;
9using Nuke.Common.Utilities;
10using Nuke.Common.Utilities.Collections;
11using Nuke.Common.IO;
12using Serilog;
13
14namespace Nuke.Cola;
15
16public static class PathExtensions
17{
18 /// <summary>
19 /// A more comfortable way to make a string into an absolute path
20 /// </summary>
21 public static AbsolutePath AsPath(this string input) => AbsolutePath.Create(input);
22
23 /// <summary>
24 /// A safer version of AsPath, use it when input may not be a valid absolute path
25 /// </summary>
26 public static AbsolutePath? TryAsPath(this string? input)
27 => !string.IsNullOrWhiteSpace(input) && Path.IsPathRooted(input)
28 ? AbsolutePath.Create(input)
29 : null;
30
31 /// <summary>
32 /// A safer version of AsPath, use it when input may not be a valid string. Input can be a
33 /// relative folder to `root`
34 /// </summary>
35 public static AbsolutePath? TryAsRelativePath(this string? input, AbsolutePath? root = null)
36 => string.IsNullOrWhiteSpace(input)
37 ? null
38 : Path.IsPathRooted(input)
39 ? AbsolutePath.Create(input)
40 : (root ?? NukeBuild.RootDirectory) / input;
41
42 /// <summary>
43 /// Shorthand for deleting both file or folder, we don't care
44 /// </summary>
45 public static void Delete(this AbsolutePath path)
46 {
47 if (path.FileExists()) path.DeleteFile();
48 if (path.DirectoryExists()) path.DeleteDirectory();
49 }
50
51 /// <summary>
52 /// Shorthand for getting a FileSystemInfo both of a file or folder, we don't care
53 /// </summary>
54 public static FileSystemInfo? AsInfo(this AbsolutePath path)
55 {
56 if (path.FileExists())
57 return new FileInfo(path);
58 if (path.DirectoryExists())
59 return new DirectoryInfo(path);
60 return null;
61 }
62
63 /// <summary>
64 /// If the given path happens to be a symlink then return its info, return null otherwise.
65 /// </summary>
66 /// <param name="link"></param>
67 /// <returns></returns>
68 public static FileSystemInfo? AsLinkInfo(this AbsolutePath link)
69 {
70 var info = link.AsInfo();
71 return info?.LinkTarget == null ? null : info;
72 }
73
74 private static FileSystemInfo LinkBoilerplate(
75 AbsolutePath link, AbsolutePath real,
76 Action assertRealExists,
77 Func<string, string, FileSystemInfo> createLink
78 ) {
79 var info = link.AsLinkInfo();
80 var linkTarget = info?.ResolveLinkTarget(true)?.FullName;
81 if (linkTarget == real)
82 {
83 Log.Information("Link already exists {1} <- {0}", link, real);
84 return info!;
85 }
86 if (linkTarget != null)
87 {
88 Log.Warning("Existing link points to somewhere else {1} <- {0}", link, linkTarget);
89 Log.Warning(" Replacing target with {0}", real);
90 link.Delete();
91 }
92 else Log.Information("Linking {1} <- {0}", link, real);
93
94 assertRealExists();
95 Assert.False(link.FileExists() || link.DirectoryExists());
96
97 if (!link.Parent.DirectoryExists())
98 link.Parent.CreateDirectory();
99
100 var realRelative = link.Parent.GetRelativePathTo(real);
101
102 return createLink(link, realRelative);
103 }
104
105 /// <summary>
106 /// Create a new symlink at this location referring to another file
107 /// </summary>
108 /// <param name="link">The location of the link</param>
109 /// <param name="real">The location of the real item</param>
110 public static FileSystemInfo LinksFile(this AbsolutePath link, AbsolutePath real)
111 => LinkBoilerplate(
112 link, real,
113 () => Assert.FileExists(real),
114 File.CreateSymbolicLink
115 );
116
117 /// <summary>
118 /// Create a new symlink referring to this file at given location
119 /// </summary>
120 /// <param name="real">The location of the real item</param>
121 /// <param name="link">The location of the link</param>
122 public static FileSystemInfo LinkedByFile(this AbsolutePath real, AbsolutePath link)
123 => LinksFile(link, real);
124
125 /// <summary>
126 /// Create a new symlink at this location referring to another folder
127 /// </summary>
128 /// <param name="link">The location of the link</param>
129 /// <param name="real">The location of the real item</param>
130 public static FileSystemInfo LinksDirectory(this AbsolutePath link, AbsolutePath real)
131 => LinkBoilerplate(
132 link, real,
133 () => Assert.DirectoryExists(real),
134 Directory.CreateSymbolicLink
135 );
136
137 /// <summary>
138 /// Create a new symlink referring to this folder at given location
139 /// </summary>
140 /// <param name="real">The location of the real item</param>
141 /// <param name="link">The location of the link</param>
142 public static FileSystemInfo LinkedByDirectory(this AbsolutePath real, AbsolutePath link)
143 => LinksDirectory(link, real);
144
145
146 /// <summary>
147 /// Create a new symlink at this location referring to given item
148 /// </summary>
149 /// <param name="link">The location of the link</param>
150 /// <param name="real">The location of the real item</param>
151 public static FileSystemInfo Links(this AbsolutePath link, AbsolutePath real)
152 {
153 Assert.True(real.FileExists() || real.DirectoryExists());
154 if (real.FileExists()) return link.LinksFile(real);
155 return link.LinksDirectory(real);
156 }
157
158 /// <summary>
159 /// Create a new symlink referring to this item at given location
160 /// </summary>
161 /// <param name="real">The location of the real item</param>
162 /// <param name="link">The location of the link</param>
163 public static FileSystemInfo LinkedBy(this AbsolutePath real, AbsolutePath link)
164 => Links(link, real);
165
166 /// <summary>
167 /// Create a new symlink referring to this item at given location, or if that fails for any
168 /// reason attempt to copy this item to given location
169 /// </summary>
170 /// <param name="real"></param>
171 /// <param name="link"></param>
172 /// <param name="policy"></param>
173 public static Attempt LinkedByOrCopyTo(this AbsolutePath real, AbsolutePath link, ExistsPolicy policy = ExistsPolicy.Fail)
174 => ErrorHandling.Try(() => real.LinkedBy(link))
175 .Else(() => real.Copy(link, policy));
176
177 public static IEnumerable<AbsolutePath> SubTree(this AbsolutePath origin, Func<AbsolutePath, bool>? filter = null)
178 => origin.DescendantsAndSelf(d =>
179 from sd in d.GlobDirectories("*")
180 where filter?.Invoke(sd) ?? true
181 select sd
182 );
183
184 /// <summary>
185 /// Get the root of input AbsolutePath. On Windows this may be getting the "drive-letter" of
186 /// input path.
187 /// </summary>
188 public static AbsolutePath GetRoot(this AbsolutePath path) => Path.GetPathRoot(path)!.AsPath();
189
190 public static bool LookAroundFor(Func<string, bool> predicate, out AbsolutePath? result, Func<AbsolutePath, bool>? directoryFilter = null, AbsolutePath? rootDirectory = null)
191 {
192 result = null;
193 rootDirectory ??= NukeBuild.RootDirectory;
194
195 var parents = rootDirectory
196 .DescendantsAndSelf(d => d.Parent, d => Path.GetPathRoot(d) != d );
197
198 foreach(var p in parents)
199 foreach(var f in Directory.EnumerateFiles(p))
200 {
201 if(predicate(f))
202 {
203 result = (AbsolutePath) f;
204 return true;
205 }
206 }
207
208 foreach(var p in rootDirectory.SubTree(directoryFilter))
209 foreach(var f in Directory.EnumerateFiles(p))
210 {
211 if(predicate(f))
212 {
213 result = (AbsolutePath) f;
214 return true;
215 }
216 }
217 return false;
218 }
219
220 public static AbsolutePath? GetVersionSubfolder(this AbsolutePath root, string version)
221 {
222 if (Path.IsPathRooted(version))
223 {
224 return (AbsolutePath) version;
225 }
226 if ((root / version).DirectoryExists())
227 {
228 return root / version;
229 }
230 version = version.Contains('.') ? version : version + ".0";
231 if (Version.TryParse(version, out var semVersion))
232 {
233 var majorMinorPatch = $"{semVersion.Major}.{semVersion.Minor}.{semVersion.Build}".Replace("-1", "0");
234 var majorMinor = $"{semVersion.Major}.{semVersion.Minor}".Replace("-1", "0");
235 var candidate =
236 root.GlobDirectories("*").FirstOrDefault(d => d.Name.Contains(majorMinorPatch))
237 ?? root.GlobDirectories("*").FirstOrDefault(d => d.Name.Contains(majorMinor))
238 ?? root.GlobDirectories("*").FirstOrDefault(d => d.Name.Contains(semVersion.Major.ToString()));
239 return candidate;
240 }
241 return null;
242 }
243}
static Attempt Try(Action action, Action< Exception >? onFailure=null, Exception[]? previousErrors=null)
Attempt to try something which may throw an exception. If an exception is thrown then wrap it inside ...
static Attempt LinkedByOrCopyTo(this AbsolutePath real, AbsolutePath link, ExistsPolicy policy=ExistsPolicy.Fail)
Create a new symlink referring to this item at given location, or if that fails for any reason attemp...
static ? FileSystemInfo AsInfo(this AbsolutePath path)
Shorthand for getting a FileSystemInfo both of a file or folder, we don't care.
static ? AbsolutePath TryAsRelativePath(this string? input, AbsolutePath? root=null)
A safer version of AsPath, use it when input may not be a valid string. Input can be a relative folde...
static ? FileSystemInfo AsLinkInfo(this AbsolutePath link)
If the given path happens to be a symlink then return its info, return null otherwise.
static void Delete(this AbsolutePath path)
Shorthand for deleting both file or folder, we don't care.
static FileSystemInfo Links(this AbsolutePath link, AbsolutePath real)
Create a new symlink at this location referring to given item.
static FileSystemInfo LinksFile(this AbsolutePath link, AbsolutePath real)
Create a new symlink at this location referring to another file.
static FileSystemInfo LinksDirectory(this AbsolutePath link, AbsolutePath real)
Create a new symlink at this location referring to another folder.
static FileSystemInfo LinkedByFile(this AbsolutePath real, AbsolutePath link)
Create a new symlink referring to this file at given location.
static AbsolutePath GetRoot(this AbsolutePath path)
Get the root of input AbsolutePath. On Windows this may be getting the "drive-letter" of input path.
static ? AbsolutePath TryAsPath(this string? input)
A safer version of AsPath, use it when input may not be a valid absolute path.
static FileSystemInfo LinkedByDirectory(this AbsolutePath real, AbsolutePath link)
Create a new symlink referring to this folder at given location.
static FileSystemInfo LinkedBy(this AbsolutePath real, AbsolutePath link)
Create a new symlink referring to this item at given location.
static AbsolutePath AsPath(this string input)
A more comfortable way to make a string into an absolute path.
record class Attempt(Exception[]? Error=null)
A record that can represent an attempt at an arbitrary action.