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 var regex = glob.GlobToRegex();
93 var match = Regex.Match(relPath, regex);
94 for (int i = 1; i < match.Groups.Count; i++)
95 {
96 asResult = asResult.Replace($"${i}", match.Groups[i]?.Value);
97 }
98
99 return dstRoot / asResult.Replace("//", "/");
100 }
101 else return dstRoot / asExpr;
102 }
103
104 public FileOrDirectory Clone()
105 {
106 return new()
107 {
108 File = File,
110 Not = [.. Not],
111 As = As,
114 };
115 }
116
117 object ICloneable.Clone()
118 {
119 return Clone();
120 }
121}
122
123/// <summary>
124/// Controls how a folder should be exported for composition.
125/// It is meant to be used with export.yml YAML files (or export manifest files).
126/// </summary>
127public class ExportManifest : ICloneable<ExportManifest>
128{
129 /// <summary>
130 /// A list of items which will be symlinked. Content processing will obviously not happen in this case.
131 /// </summary>
132 [YamlMember]
133 public List<FileOrDirectory> Link = [];
134
135 /// <summary>
136 /// A list of items which will be copied. Content processing can happen in this case if item is
137 /// flagged to do so.
138 /// </summary>
139 [YamlMember]
140 public List<FileOrDirectory> Copy = [];
141
142 /// <summary>
143 /// A list of folders which should contain an export manifest, or files which points to export
144 /// manifests. If a given folder doesn't contain an export.yml or the given file is not an
145 /// export.yml then those will be ignored with noop.
146 /// ProcessContent is ignored in this list as that's controlled by the imported manifests.
147 /// Globbing is also supported, simply writing `**` in Directory (dir) will import all subfolders
148 /// containing an `export.yml` manifest file.
149 /// </summary>
150 [YamlMember]
151 public List<FileOrDirectory> Use = [];
152
153 /// <summary>
154 /// Ignore files or directories matching any of these patterns from this entire export
155 /// </summary>
156 [YamlMember]
157 public List<string> Not = [];
158
159 /// <summary>
160 /// Merge one manifest with another. This will simply append items to each lists.
161 /// </summary>
162 public void Add(ExportManifest? other)
163 {
164 if (other == null) return;
165 Link.AddRange(other.Link);
166 Copy.AddRange(other.Copy);
167 Use.AddRange(other.Use);
168 Not.AddRange(other.Not);
169 }
170
171 public ExportManifest Clone()
172 {
173 return new()
174 {
175 Link = [.. Link.Select(s => s.Clone())],
176 Copy = [.. Copy.Select(s => s.Clone())],
177 Use = [.. Use.Select(s => s.Clone())],
178 Not = [.. Not],
179 };
180 }
181
182 object ICloneable.Clone()
183 {
184 return Clone();
185 }
186}
187
188public static class ExportManifestExtensions
189{
190 /// <summary>
191 /// Combine input export manifests together into given one. If given is null, the first one will
192 /// be cloned from the others-
193 /// </summary>
194 /// <returns>
195 /// Return `self`, or the first valid export manifest in `others`. For this reason the return
196 /// object might not be the same as `self` if `self` was originally null.
197 /// </returns>
198 public static ExportManifest? Combine(this ExportManifest? self, IEnumerable<ExportManifest>? others)
199 {
200 if (others == null || others.IsEmpty()) return self;
201 if (self == null)
202 {
203 self = others.First().Clone();
204 others = others.Skip(1);
205 }
206 foreach (var other in others)
207 {
208 self!.Add(other);
209 }
210 return self;
211 }
212}
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.