Skip to content

Commit d34feea

Browse files
committed
Archive manifest
1 parent d302758 commit d34feea

27 files changed

+350
-182
lines changed

src/Yuka.Cli/Command.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,18 @@ protected void Error(string text) {
6666
public static Command[] AvailableCommands => CommandFactories.Values.Select(factory => factory(CommandParameters.Empty)).ToArray();
6767

6868
public static Command CreateFromName(string name, CommandParameters parameters) {
69-
return CommandFactories.TryGet(name, param => null)(parameters);
69+
return CommandFactories.TryGet(name, _ => null)(parameters);
70+
}
71+
72+
public static bool TryRun(string[] cliArgs) {
73+
var commandLine = CommandParameters.ParseArguments(cliArgs);
74+
if(commandLine.Arguments.Count == 0) return false;
75+
76+
var command = CreateFromName(commandLine.Arguments[0], commandLine);
77+
if(command == null) return false;
78+
79+
command.Execute();
80+
return true;
7081
}
7182

7283
#endregion

src/Yuka.Cli/CommandParameters.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class CommandParameters {
77
public readonly List<string> Arguments = new List<string>();
88
public readonly Dictionary<string, string> Flags = new Dictionary<string, string>();
99

10-
public CommandParameters(string[] cliArgs) {
10+
public CommandParameters(IEnumerable<string> cliArgs) {
1111
foreach(string argument in cliArgs) {
1212
if(argument.StartsWith("-")) {
1313
ParseFlag(argument);
@@ -114,7 +114,7 @@ public string GetString(string name, char shorthand, string fallback) {
114114

115115
public static CommandParameters Empty => new CommandParameters(Array.Empty<string>());
116116

117-
public static CommandParameters ParseArguments(string[] args) {
117+
public static CommandParameters ParseArguments(IEnumerable<string> args) {
118118
return new CommandParameters(args);
119119
}
120120

src/Yuka.Cli/Commands/CopyCommand.cs

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Linq;
5+
using Newtonsoft.Json;
46
using Yuka.Cli.Util;
57
using Yuka.IO;
68

@@ -13,7 +15,7 @@ public CopyCommand(CommandParameters parameters) : base(parameters) {
1315

1416
public override string[] Description => new[] {
1517
"Copies files from one place to another while optionally converting them in some way.",
16-
"\aacopy\a- forms the basis of most other commands."
18+
"Most other commands use copy internally."
1719
};
1820

1921
public override (string syntax, string description)[] Usage => new[] {
@@ -25,20 +27,23 @@ public override (string syntax, string description)[] Usage => new[] {
2527

2628
// reminder: when changing these, also change all inheriting classes
2729
public override (char shorthand, string name, string fallback, string description)[] Flags => new[] {
28-
('s', "source", null, "Source location"),
29-
('d', "destination", null, "Destination location"),
30-
('f', "format", "keep", "The preferred output format (valid values: \abkeep\a-, \abpacked\a-, \abunpacked\a-)"),
31-
('r', "raw", null, "Short form of \ac--format=keep\a-, overwrites the format flag if set"),
32-
('m', "move", "false", "Delete each fileName after successfully copying it"),
33-
('o', "overwrite", "false", "Delete existing destination archive/folder"),
34-
('q', "quiet", null, "Disable user-friendly output"),
35-
('v', "verbose", null, "Whether to enable detailed output"),
36-
('w', "wait", null, "Whether to wait after the command finished")
30+
('s', "source", null, "Source location"),
31+
('d', "destination", null, "Destination location"),
32+
('f', "format", "keep", "The preferred output format (valid values: \abkeep\a-, \abpacked\a-, \abunpacked\a-)"),
33+
('r', "raw", null, "Short form of \ac--format=keep\a-, overwrites the format flag if set"),
34+
('m', "move", "false", "Delete each fileName after successfully copying it"),
35+
(' ', "manifest", "false", "Generate a manifest file"),
36+
('i', "ignoremanifest", "false", "Ignore the manifest, if it exists"),
37+
('o', "overwrite", "false", "Delete existing destination archive/folder"),
38+
('q', "quiet", null, "Disable user-friendly output"),
39+
('v', "verbose", null, "Whether to enable detailed output"),
40+
('w', "wait", null, "Whether to wait after the command finished")
3741
};
3842

3943
// copy modes
4044
protected FormatType _prefererredFormatType;
41-
protected bool _rawCopy, _deleteAfterCopy, _overwriteExisting;
45+
protected bool _rawCopy, _deleteAfterCopy, _overwriteExisting, _generateManifest, _ignoreManifest;
46+
protected Manifest _inputManifest;
4247

4348
public override bool Execute() {
4449
try {
@@ -47,8 +52,10 @@ public override bool Execute() {
4752

4853
CheckPaths(sourcePath, destinationPath);
4954

50-
int fileCount;
55+
Manifest outputManifest;
5156
using(var sourceFs = FileSystem.OpenExisting(sourcePath)) {
57+
_inputManifest = _ignoreManifest ? null : ReadManifest(sourceFs);
58+
5259
using(var destinationFs = FileSystem.OpenOrCreate(destinationPath, sourceFs is SingleFileSystem, _overwriteExisting)) {
5360
// collect files
5461
var files = new List<string>();
@@ -57,11 +64,12 @@ public override bool Execute() {
5764
}
5865

5966
// call copy loop
60-
fileCount = CopyFiles(sourceFs, destinationFs, files.Distinct(), _rawCopy, _deleteAfterCopy);
67+
outputManifest = CopyFiles(sourceFs, destinationFs, files.Distinct(), _rawCopy, _deleteAfterCopy);
68+
if(_generateManifest) WriteManifest(outputManifest, destinationFs);
6169
}
6270
}
6371

64-
Success($"Successfully copied {fileCount} files");
72+
Success($"Successfully copied {outputManifest.Count} files");
6573
}
6674
catch(Exception e) when(!System.Diagnostics.Debugger.IsAttached) {
6775
Error($"{e.GetType().Name}: {e.Message}");
@@ -71,6 +79,19 @@ public override bool Execute() {
7179
return true;
7280
}
7381

82+
protected virtual void WriteManifest(Manifest manifest, FileSystem fs) {
83+
using(var writer = new JsonTextWriter(new StreamWriter(fs.CreateFile(".manifest")))) {
84+
new JsonSerializer { Formatting = Formatting.Indented }.Serialize(writer, manifest);
85+
}
86+
}
87+
88+
protected virtual Manifest ReadManifest(FileSystem fs) {
89+
if(!fs.FileExists(".manifest")) return null;
90+
using(var reader = new JsonTextReader(new StreamReader(fs.OpenFile(".manifest")))) {
91+
return new JsonSerializer { Formatting = Formatting.Indented }.Deserialize<Manifest>(reader);
92+
}
93+
}
94+
7495
protected virtual (string sourcePath, string destinationPath, string[] filters) GetPaths() {
7596
string sourcePath, destinationPath;
7697
string[] filters = { "*.*" };
@@ -118,6 +139,8 @@ protected virtual void SetCopyModes() {
118139
string format = Parameters.GetString("format", 'f', FormatTypeFallback).ToLower();
119140
_rawCopy = Parameters.GetBool("raw", 'r', false);
120141
_deleteAfterCopy = Parameters.GetBool("move", 'm', false);
142+
_generateManifest = Parameters.GetBool("manifest", false);
143+
_ignoreManifest = Parameters.GetBool("ignoremanifest", 'i', false);
121144
_overwriteExisting = Parameters.GetBool("overwrite", 'o', false);
122145

123146
switch(format) {
@@ -139,46 +162,67 @@ protected virtual void SetCopyModes() {
139162
}
140163

141164
protected virtual FormatPreference GetOutputFormat(object obj, string fileName, Format fileFormat) {
165+
if(_inputManifest != null) {
166+
foreach(var (source, target) in _inputManifest) {
167+
// find the entry that produced this file
168+
if(target.Any(pair => pair.Name == fileName)) {
169+
// return the original format
170+
return new FormatPreference(source[0].Format, _prefererredFormatType);
171+
}
172+
}
173+
}
142174
return new FormatPreference(null, _prefererredFormatType);
143175
}
144176

145-
protected virtual (object obj, Format fileFormat) ReadFile(FileSystem sourceFs, string fileName) {
146-
return FileReader.DecodeObject(fileName, sourceFs, true);
177+
protected virtual (object obj, Format fileFormat) ReadFile(FileSystem sourceFs, string fileName, FileList files) {
178+
return FileReader.DecodeObject(fileName, sourceFs, files, true);
147179
}
148180

149-
protected virtual Format WriteFile(object obj, Format inputFormat, FileSystem destinationFs, string fileName) {
150-
return FileWriter.EncodeObject(obj, fileName, destinationFs, GetOutputFormat(obj, fileName, inputFormat));
181+
protected virtual void WriteFile(object obj, Format inputFormat, FileSystem destinationFs, string fileName, FileList files) {
182+
FileWriter.EncodeObject(obj, fileName, destinationFs, GetOutputFormat(obj, fileName, inputFormat), files);
151183
}
152184

153-
protected virtual int CopyFiles(FileSystem sourceFs, FileSystem destinationFs, IEnumerable<string> files, bool rawCopy, bool deleteAfterCopy) {
185+
protected virtual Manifest CopyFiles(FileSystem sourceFs, FileSystem destinationFs, IEnumerable<string> files, bool rawCopy, bool deleteAfterCopy) {
154186
bool verbose = Parameters.GetBool("verbose", 'v', false);
155-
int fileCount = 0;
187+
188+
var manifest = new Manifest();
156189

157190
// main loop
158191
foreach(string fileName in files) {
192+
if(fileName == ".manifest") continue;
193+
159194
if(rawCopy) {
160195
Log($"Copying \ae{fileName}");
161196
using(var sourceStream = sourceFs.OpenFile(fileName)) {
162197
using(var destinationStream = destinationFs.CreateFile(fileName)) {
163198
sourceStream.CopyTo(destinationStream);
164-
fileCount++;
199+
200+
manifest.Add(
201+
new FileList { (fileName, Format.Raw) },
202+
new FileList { (fileName, Format.Raw) }
203+
);
165204
}
166205
}
167206
}
168207
else {
169208

170-
var (obj, fileFormat) = ReadFile(sourceFs, fileName);
209+
var readFiles = new FileList();
210+
var writtenFiles = new FileList();
211+
212+
var (obj, fileFormat) = ReadFile(sourceFs, fileName, readFiles);
171213

172214
if(obj == null) {
173-
if(verbose) Output.WriteLine($"Skipping {fileName}", ConsoleColor.Yellow);
215+
//if(verbose) Output.WriteLine($"Skipping {fileName}", ConsoleColor.Yellow);
174216
continue;
175217
}
218+
//if(verbose) Output.WriteLine($"Processing {fileName}", ConsoleColor.Yellow);
176219

177-
Log($"Decoded \ae{fileName}\a- to \ab{obj.GetType().Name}");
220+
Log($"Decoded [{string.Join(", ", readFiles.Select(pair => $"\ab{pair.Format.Name} \ae{pair.Name}\a-"))}] to \ab{obj.GetType().Name}");
178221

179-
var outputFormat = WriteFile(obj, fileFormat, destinationFs, fileName);
222+
WriteFile(obj, fileFormat, destinationFs, fileName, writtenFiles);
180223

181-
Log($"Encoded \ab{obj.GetType().Name}\a- ({outputFormat.GetType().Name})");
224+
Log($"Encoded [{string.Join(", ", writtenFiles.Select(pair => $"\ab{pair.Format.Name} \ae{pair.Name}\a-"))}]");
225+
Log("");
182226

183227
if(deleteAfterCopy) {
184228
// delete auxiliary files (csv, frm, ani, etc...)
@@ -191,11 +235,11 @@ protected virtual int CopyFiles(FileSystem sourceFs, FileSystem destinationFs, I
191235
sourceFs.DeleteFile(fileName);
192236
}
193237

194-
fileCount++;
238+
manifest.Add(readFiles, writtenFiles);
195239
}
196240
}
197241

198-
return fileCount;
242+
return manifest;
199243
}
200244
}
201245
}

src/Yuka.Cli/Commands/PackCommand.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ public override (string syntax, string description)[] Usage => new[] {
2121
};
2222

2323
public override (char shorthand, string name, string fallback, string description)[] Flags => new[] {
24-
('s', "source", null, "Source folder"),
25-
('d', "destination", null, "Destination archive"),
26-
('f', "format", "packed", "The preferred output format (valid values: \abkeep\a-, \abpacked\a-, \abunpacked\a-)"),
27-
('r', "raw", null, "Short form of \ac--format=keep\a-, overwrites the format flag if set"),
28-
('o', "overwrite", "true", "Delete existing destination archive"),
29-
('a', "append", null, "Appends files to an existing archive, equal to \ac--overwrite=false"),
30-
('q', "quiet", null, "Disable user-friendly output"),
31-
('v', "verbose", null, "Whether to enable detailed output"),
32-
('w', "wait", null, "Whether to wait after the command finished")
24+
('s', "source", null, "Source folder"),
25+
('d', "destination", null, "Destination archive"),
26+
('f', "format", "packed", "The preferred output format (valid values: \abkeep\a-, \abpacked\a-, \abunpacked\a-)"),
27+
('r', "raw", null, "Short form of \ac--format=keep\a-, overwrites the format flag if set"),
28+
('o', "overwrite", "true", "Delete existing destination archive"),
29+
('a', "append", null, "Appends files to an existing archive, equal to \ac--overwrite=false"),
30+
('i', "ignoremanifest", null, "Ignore the manifest, if it exists"),
31+
('q', "quiet", null, "Disable user-friendly output"),
32+
('v', "verbose", null, "Whether to enable detailed output"),
33+
('w', "wait", null, "Whether to wait after the command finished")
3334
};
3435

3536
protected override string DeriveDestinationPath(string sourcePath) {
@@ -52,6 +53,7 @@ protected override void SetCopyModes() {
5253

5354
// --move flag always false
5455
_deleteAfterCopy = false;
56+
_generateManifest = false;
5557
_overwriteExisting = Parameters.GetBool("overwrite", 'o', true) && !Parameters.GetBool("append", 'a', false);
5658
}
5759
}

src/Yuka.Cli/Commands/UnpackCommand.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.IO;
3+
using Newtonsoft.Json;
24
using Yuka.IO;
35
using Yuka.Util;
46

@@ -21,14 +23,15 @@ public override (string syntax, string description)[] Usage => new[] {
2123
};
2224

2325
public override (char shorthand, string name, string fallback, string description)[] Flags => new[] {
24-
('s', "source", null, "Source archive"),
25-
('d', "destination", null, "Destination folder"),
26-
('f', "format", "unpacked", "The preferred output format (valid values: \abkeep\a-, \abpacked\a-, \abunpacked\a-)"),
27-
('r', "raw", null, "Short form of \ac--format=keep\a-, overwrites the format flag if set"),
28-
('o', "overwrite", "false", "Delete existing destination folder"),
29-
('q', "quiet", null, "Disable user-friendly output"),
30-
('v', "verbose", null, "Whether to enable detailed output"),
31-
('w', "wait", null, "Whether to wait after the command finished")
26+
('s', "source", null, "Source archive"),
27+
('d', "destination", null, "Destination folder"),
28+
('f', "format", "unpacked", "The preferred output format (valid values: \abkeep\a-, \abpacked\a-, \abunpacked\a-)"),
29+
('r', "raw", null, "Short form of \ac--format=keep\a-, overwrites the format flag if set"),
30+
('m', "manifest", "true", "Generate a manifest file"), // -m shadows the --move flag, which is not available for the unpack command
31+
('o', "overwrite", "false", "Delete existing destination folder"),
32+
('q', "quiet", null, "Disable user-friendly output"),
33+
('v', "verbose", null, "Whether to enable detailed output"),
34+
('w', "wait", null, "Whether to wait after the command finished")
3235
};
3336

3437
protected override string DeriveDestinationPath(string sourcePath) {
@@ -51,6 +54,8 @@ protected override void SetCopyModes() {
5154

5255
// --move flag always false
5356
_deleteAfterCopy = false;
57+
_ignoreManifest = true;
58+
_generateManifest = Parameters.GetBool("manifest", 'm', true);
5459
}
5560
}
5661
}

src/Yuka.Cli/FodyWeavers.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Weavers>
3-
<Costura />
43
</Weavers>

src/Yuka.Cli/Program.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,11 @@ namespace Yuka.Cli {
2222

2323
public class Program {
2424
public static void Main(string[] args) {
25-
var commandLine = CommandParameters.ParseArguments(args);
26-
string commandName = GetCommandName(commandLine);
27-
var command = Command.CreateFromName(commandName, commandLine);
28-
29-
if(command != null) {
30-
command.Execute();
31-
}
32-
else {
25+
if(!Command.TryRun(args)) {
3326
new HelpCommand(CommandParameters.Empty).Execute();
3427
}
35-
3628
Output.Flush();
3729
}
38-
39-
public static string GetCommandName(CommandParameters parameters) {
40-
return parameters.Arguments.Count > 0 ? parameters.Arguments[0] : "help";
41-
}
4230
}
4331

4432
#else

src/Yuka.Cli/Yuka.Cli.csproj

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,17 @@
3434
<WarningLevel>4</WarningLevel>
3535
</PropertyGroup>
3636
<ItemGroup>
37-
<Reference Include="Costura, Version=1.6.2.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
38-
<HintPath>..\packages\Costura.Fody.1.6.2\lib\dotnet\Costura.dll</HintPath>
39-
<Private>False</Private>
37+
<Reference Include="Costura, Version=2.0.1.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
38+
<HintPath>..\packages\Costura.Fody.2.0.1\lib\net452\Costura.dll</HintPath>
4039
</Reference>
41-
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
42-
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
40+
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
41+
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
4342
</Reference>
4443
<Reference Include="System" />
4544
<Reference Include="System.Core" />
4645
<Reference Include="System.Drawing" />
47-
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
48-
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
46+
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
47+
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
4948
</Reference>
5049
<Reference Include="System.Xml.Linq" />
5150
<Reference Include="System.Data.DataSetExtensions" />
@@ -77,16 +76,16 @@
7776
</ProjectReference>
7877
</ItemGroup>
7978
<ItemGroup>
80-
<None Include="FodyWeavers.xml" />
79+
<Content Include="FodyWeavers.xml" />
8180
</ItemGroup>
8281
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
83-
<Import Project="..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets" Condition="Exists('..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets')" />
82+
<Import Project="..\packages\Fody.3.0.3\build\Fody.targets" Condition="Exists('..\packages\Fody.3.0.3\build\Fody.targets')" />
8483
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
8584
<PropertyGroup>
8685
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
8786
</PropertyGroup>
88-
<Error Condition="!Exists('..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets'))" />
89-
<Error Condition="!Exists('..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets'))" />
87+
<Error Condition="!Exists('..\packages\Fody.3.0.3\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.3.0.3\build\Fody.targets'))" />
88+
<Error Condition="!Exists('..\packages\Costura.Fody.2.0.1\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.2.0.1\build\Costura.Fody.targets'))" />
9089
</Target>
91-
<Import Project="..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets')" />
90+
<Import Project="..\packages\Costura.Fody.2.0.1\build\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.2.0.1\build\Costura.Fody.targets')" />
9291
</Project>

0 commit comments

Comments
 (0)