Nuke.Cola
Loading...
Searching...
No Matches
ExportManifest.cs
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text.RegularExpressions;
5using System.Threading.Tasks;
6using GlobExpressions;
7using Nuke.Cola;
8using Nuke.Common.IO;
9using Nuke.Common.Utilities;
10using Nuke.Common.Utilities.Collections;
11using Serilog;
12using YamlDotNet.Serialization;
13
15
16/// <summary>
17/// A union provided for denoting wether we want to link/copy a file or a directory.
18/// It is undefined behavior when both File and Directory is set to non-null value.
19///
20/// Addtitionally specify some options about the method of exporting given item.
21/// </summary>
22public class FileOrDirectory : ICloneable<FileOrDirectory>
23{
24 /// <summary>
25 /// Export a single or a glob of files handled individually. Either File or Directory (dir)
26 /// must be specified.
27 /// </summary>
28 [YamlMember]
29 public string? File;
30
31 /// <summary>
32 /// Export one or a glob of directories handled recursively. Files inside target directories are
33 /// not considered. Either File or Directory (dir) must be specified.
34 /// </summary>
35 [YamlMember(Alias = "dir")]
36 public string? Directory;
37
38 /// <summary>
39 /// Exclude iterms from this particular set of files or directories if they match any of these patterns
40 /// </summary>
41 [YamlMember(Alias = "not")]
42 public List<string> Not = [];
43
44 /// <summary>
45 /// Override the destination relative path of exported item.
46 ///
47 /// Use `$N` syntax (where N is 1..(number of * or **)) to reuse those captured segments of the
48 /// globbing.
49 ///
50 /// Use `$#` syntax to get the 0 based ID of globbed item.
51 /// </summary>
52 [YamlMember]
53 public string? As;
54
55 /// <summary>
56 /// When working with a file, process its contents for replacing specified suffixes
57 /// </summary>
58 [YamlMember(Alias = "procContent")]
59 public bool ProcessContent = false;
60
61 /// <summary>
62 /// Only used by "use", if a subfolder uses a different file for export manifest, specify that
63 /// via this glob. Default is "export.y*ml" or whatever else has been specified for this import
64 /// session.
65 /// </summary>
66 [YamlMember(Alias = "manifestFilePattern")]
67 public string? ManifestFilePattern;
68
69 internal AbsolutePath? GetDestination(AbsolutePath srcRoot, AbsolutePath dstRoot, AbsolutePath currentPath, int itemId, IEnumerable<string> exclude)
70 {
71 var glob = (File ?? Directory)!;
72 var relativePath = srcRoot.GetRelativePathTo(currentPath);
73
74 bool Ignore(string glob)
75 {
76 var regex = glob.GlobToRegex();
77 return Regex.IsMatch(relativePath!.ToString(), regex, RegexOptions.IgnoreCase);
78 }
79
80 if (exclude.Any(Ignore))
81 return null;
82
83 if (As == null)
84 return dstRoot / relativePath;
85
86 var asExpr = As.Replace("$#", itemId.ToString());
87
88 if (glob.Contains('*') && asExpr.Contains('$'))
89 {
90 var asResult = asExpr;
91 var relPath = relativePath.ToString().Replace("\\", "/");
92 relPath = relPath.Contains('/') ? relPath : "/" + relPath;
93
94 var regex = glob.GlobToRegex();
95 var match = Regex.Match(relPath, regex);
96 for (int i = 1; i < match.Groups.Count; i++)
97 {
98 asResult = asResult.Replace($"${i}", match.Groups[i]?.Value);
99 }
100
101 return dstRoot / asResult.Replace("//", "/");
102 }
103 else return dstRoot / asExpr;
104 }
105
106 public FileOrDirectory Clone()
107 {
108 return new()
109 {
110 File = File,
112 Not = [.. Not],
113 As = As,
116 };
117 }
118
119 object ICloneable.Clone()
120 {
121 return Clone();
122 }
123}
124
125/// <summary>
126/// Controls how a folder should be exported for composition.
127/// It is meant to be used with export.yml YAML files (or export manifest files).
128/// </summary>
129public class ExportManifest : ICloneable<ExportManifest>
130{
131 /// <summary>
132 /// A list of items which will be symlinked. Content processing will obviously not happen in this case.
133 /// </summary>
134 [YamlMember]
135 public List<FileOrDirectory> Link = [];
136
137 /// <summary>
138 /// A list of items which will be copied. Content processing can happen in this case if item is
139 /// flagged to do so.
140 /// </summary>
141 [YamlMember]
142 public List<FileOrDirectory> Copy = [];
143
144 /// <summary>
145 /// A list of folders which should contain an export manifest, or files which points to export
146 /// manifests. If a given folder doesn't contain an export.yml or the given file is not an
147 /// export.yml then those will be ignored with noop.
148 /// ProcessContent is ignored in this list as that's controlled by the imported manifests.
149 /// Globbing is also supported, simply writing `**` in Directory (dir) will import all subfolders
150 /// containing an `export.yml` manifest file.
151 /// </summary>
152 [YamlMember]
153 public List<FileOrDirectory> Use = [];
154
155 /// <summary>
156 /// Ignore files or directories matching any of these patterns from this entire export
157 /// </summary>
158 [YamlMember]
159 public List<string> Not = [];
160
161 /// <summary>
162 /// Merge one manifest with another. This will simply append items to each lists.
163 /// </summary>
164 public void Add(ExportManifest? other)
165 {
166 if (other == null) return;
167 Link.AddRange(other.Link);
168 Copy.AddRange(other.Copy);
169 Use.AddRange(other.Use);
170 Not.AddRange(other.Not);
171 }
172
173 public ExportManifest Clone()
174 {
175 return new()
176 {
177 Link = [.. Link.Select(s => s.Clone())],
178 Copy = [.. Copy.Select(s => s.Clone())],
179 Use = [.. Use.Select(s => s.Clone())],
180 Not = [.. Not],
181 };
182 }
183
184 object ICloneable.Clone()
185 {
186 return Clone();
187 }
188}
189
190public static class ExportManifestExtensions
191{
192 /// <summary>
193 /// Combine input export manifests together into given one. If given is null, the first one will
194 /// be cloned from the others-
195 /// </summary>
196 /// <returns>
197 /// Return `self`, or the first valid export manifest in `others`. For this reason the return
198 /// object might not be the same as `self` if `self` was originally null.
199 /// </returns>
200 public static ExportManifest? Combine(this ExportManifest? self, IEnumerable<ExportManifest>? others)
201 {
202 if (others == null || others.IsEmpty()) return self;
203 if (self == null)
204 {
205 self = others.First().Clone();
206 others = others.Skip(1);
207 }
208 foreach (var other in others)
209 {
210 self!.Add(other);
211 }
212 return self;
213 }
214}
static ? ExportManifest Combine(this ExportManifest? self, IEnumerable< ExportManifest >? others)
Combine input export manifests together into given one. If given is null, the first one will be clone...
Controls how a folder should be exported for composition. It is meant to be used with export....
List< FileOrDirectory > Copy
A list of items which will be copied. Content processing can happen in this case if item is flagged t...
void Add(ExportManifest? other)
Merge one manifest with another. This will simply append items to each lists.
List< string > Not
Ignore files or directories matching any of these patterns from this entire export.
List< FileOrDirectory > Use
A list of folders which should contain an export manifest, or files which points to export manifests....
List< FileOrDirectory > Link
A list of items which will be symlinked. Content processing will obviously not happen in this case.
A union provided for denoting wether we want to link/copy a file or a directory. It is undefined beha...
string? File
Export a single or a glob of files handled individually. Either File or Directory (dir) must be speci...
string? As
Override the destination relative path of exported item.
string? ManifestFilePattern
Only used by "use", if a subfolder uses a different file for export manifest, specify that via this g...
List< string > Not
Exclude iterms from this particular set of files or directories if they match any of these patterns.
string? Directory
Export one or a glob of directories handled recursively. Files inside target directories are not cons...
bool ProcessContent
When working with a file, process its contents for replacing specified suffixes.