Skip to content

Commit 4f9389d

Browse files
committed
Change behaviour of adding Steps
A step is now considered to live in a dedicated `Resource` package: Creating a step `Soldering` would create a package `<project>.Resources.Soldering` and put all related resource files into that project.
1 parent dfcd5dc commit 4f9389d

File tree

20 files changed

+368
-69
lines changed

20 files changed

+368
-69
lines changed

Moryx.Cli.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
2020
Directory.Build.targets = Directory.Build.targets
2121
EndProjectSection
2222
EndProject
23+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.Cli.Commands.Tests", "src\Tests\Moryx.Cli.Commands.Tests\Moryx.Cli.Commands.Tests.csproj", "{4C082129-3A6D-4A72-80DC-8A9D3A71A79F}"
24+
EndProject
2325
Global
2426
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2527
Debug|Any CPU = Debug|Any CPU
@@ -46,12 +48,17 @@ Global
4648
{F6E69A2A-F183-4A01-8629-CEBBB0417ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
4749
{F6E69A2A-F183-4A01-8629-CEBBB0417ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
4850
{F6E69A2A-F183-4A01-8629-CEBBB0417ED6}.Release|Any CPU.Build.0 = Release|Any CPU
51+
{4C082129-3A6D-4A72-80DC-8A9D3A71A79F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52+
{4C082129-3A6D-4A72-80DC-8A9D3A71A79F}.Debug|Any CPU.Build.0 = Debug|Any CPU
53+
{4C082129-3A6D-4A72-80DC-8A9D3A71A79F}.Release|Any CPU.ActiveCfg = Release|Any CPU
54+
{4C082129-3A6D-4A72-80DC-8A9D3A71A79F}.Release|Any CPU.Build.0 = Release|Any CPU
4955
EndGlobalSection
5056
GlobalSection(SolutionProperties) = preSolution
5157
HideSolutionNode = FALSE
5258
EndGlobalSection
5359
GlobalSection(NestedProjects) = preSolution
5460
{E65D2591-A889-4F42-A6F5-AECAFB77DBB0} = {6FC4CD2F-8BFA-4EC1-84A9-D3670BA92E4D}
61+
{4C082129-3A6D-4A72-80DC-8A9D3A71A79F} = {6FC4CD2F-8BFA-4EC1-84A9-D3670BA92E4D}
5562
EndGlobalSection
5663
GlobalSection(ExtensibilityGlobals) = postSolution
5764
SolutionGuid = {99BD1616-551D-473A-A6DB-6A9C0F030AB7}

src/Moryx.Cli.Commands/AddResources.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,27 @@ public static class AddResources
99
public static CommandResult Exec(TemplateSettings settings, IEnumerable<string> resources)
1010
{
1111
return CommandBase.Exec(settings, (filenames)
12-
=> resources.Select(resource => AddThing.Exec(
13-
settings,
14-
new AddConfig
12+
=> resources.Select(resource =>
13+
{
14+
var addConfig = new AddConfig
1515
{
1616
SolutionName = settings.AppName,
1717
ThingName = $"{resource}Resource",
1818
Thing = "resource",
19-
ThingPlaceholders =
20-
[
21-
Template.Template.ResourcePlaceholder,
19+
ThingPlaceholders =
20+
[
21+
Template.Template.ResourcePlaceholder,
2222
Template.Template.ResourcePlaceholder2
23-
],
24-
},
25-
filenames.Resource()
26-
))
27-
.ToArray()
28-
.Flatten());
23+
],
24+
};
25+
return AddThing.Exec(
26+
settings,
27+
addConfig,
28+
filenames.Resource()
29+
);
30+
})
31+
.ToArray()
32+
.Flatten());
2933
}
3034
}
3135
}

src/Moryx.Cli.Commands/AddStep.cs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,49 @@
1-
using Moryx.Cli.Template;
1+
using Microsoft.Build.Construction;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.MSBuild;
4+
using Moryx.Cli.Commands.Components;
5+
using Moryx.Cli.Template;
26
using Moryx.Cli.Template.Models;
7+
using Moryx.Configuration;
8+
using System.Diagnostics;
39

410
namespace Moryx.Cli.Commands
511
{
612
public static class AddStep
713
{
814
public static CommandResult Exec(TemplateSettings settings, string step)
915
{
10-
return CommandBase.Exec(settings, (filenames)
11-
=> AddThing.Exec(
16+
return CommandBase.Exec(settings, (filenames) =>
17+
{
18+
var addConfig = new AddConfig
19+
{
20+
SolutionName = settings.AppName,
21+
ThingName = step,
22+
Thing = "step",
23+
ThingPlaceholders = [Template.Template.StepPlaceholder],
24+
};
25+
var namespacePlaceholder = new Dictionary<string, string> {
26+
27+
{ $"{Template.Template.AppPlaceholder}.Resources", $"{settings.AppName}.Resources.{step}" },
28+
{ $"{settings.AppName}.Resources", $"{settings.AppName}.Resources.{step}" }
29+
};
30+
var replacements = new StringReplacements(addConfig)
31+
.AddFileNamePatterns(namespacePlaceholder)
32+
.AddContentPatterns(namespacePlaceholder)
33+
;
34+
35+
return AddThing.Exec(
1236
settings,
13-
new AddConfig
37+
addConfig,
38+
filenames.Step(),
39+
(createdFiles) =>
1440
{
15-
SolutionName = settings.AppName,
16-
ThingName = step,
17-
Thing = "step",
18-
ThingPlaceholders = [Template.Template.StepPlaceholder],
41+
var projectFileName = createdFiles.FirstOrDefault(f => f.EndsWith(".csproj"));
42+
SolutionFileManipulation.AddProjectToSolution(settings, projectFileName);
1943
},
20-
filenames.Step()
21-
));
44+
replacements
45+
);
46+
});
2247
}
2348
}
2449
}

src/Moryx.Cli.Commands/AddSteps.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,9 @@ public static class AddSteps
99
public static CommandResult Exec(TemplateSettings settings, IEnumerable<string> cells)
1010
{
1111
return CommandBase.Exec(settings, (filenames)
12-
=> cells.Select(step => AddThing.Exec(
12+
=> cells.Select(step => AddStep.Exec(
1313
settings,
14-
new AddConfig
15-
{
16-
SolutionName = settings.AppName,
17-
ThingName = step,
18-
Thing = "step",
19-
ThingPlaceholders = [Template.Template.StepPlaceholder],
20-
},
21-
filenames.Step()
14+
step
2215
))
2316
.ToArray()
2417
.Flatten());

src/Moryx.Cli.Commands/AddThing.cs

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,45 @@
1-
using Moryx.Cli.Commands.Extensions;
1+
using Castle.Components.DictionaryAdapter;
2+
using Microsoft.CodeAnalysis.FlowAnalysis;
3+
using Moryx.Cli.Commands.Extensions;
24
using Moryx.Cli.Template;
35
using Moryx.Cli.Template.Models;
46

57
namespace Moryx.Cli.Commands
68
{
79
public static class AddThing
810
{
9-
public static CommandResult Exec(TemplateSettings settings, AddConfig config, List<string> resourceNames, Action<IEnumerable<string>>? onAddedFiles = null)
11+
/// <summary>
12+
/// Retrieves files of a certain category from the template and moves them as defined
13+
/// in <paramref name="filenames"/> to the current project.
14+
/// </summary>
15+
/// <param name="settings"></param>
16+
/// <param name="config"></param>
17+
/// <param name="resourceNames"></param>
18+
/// <param name="onAddedFiles"></param>
19+
/// <returns></returns>
20+
public static CommandResult Exec(TemplateSettings settings, AddConfig config, List<string> resourceNames, Action<IEnumerable<string>>? onAddedFiles = null, StringReplacements? replacements = null)
1021
{
1122
return CommandBase.Exec(settings, (filenames) =>
1223
{
1324
var projectFilenames = filenames.InitialProjects();
1425

1526
try
1627
{
28+
if(replacements == null)
29+
{
30+
replacements = new StringReplacements(config);
31+
}
1732
var dictionary = Template.Template.PrepareFileStructure(config.SolutionName, resourceNames, projectFilenames);
1833

1934
var files = Template.Template.WriteFilesToDisk(
2035
dictionary,
2136
settings,
22-
s => s.Replace(config.ThingPlaceholders, config.ThingName).Replace(Template.Template.AppPlaceholder, config.SolutionName));
23-
24-
var replaceHolders = config.ThingPlaceholders
25-
.ToDictionary(s => s, s => config.ThingName);
26-
replaceHolders.Add(Template.Template.AppPlaceholder, config.SolutionName);
37+
s => s.Replace(replacements.FileNamePatterns)
38+
);
2739

2840
Template.Template.ReplacePlaceHoldersInsideFiles(
2941
files,
30-
replaceHolders);
42+
replacements.FileContentPatterns);
3143

3244
onAddedFiles?.Invoke(files);
3345
}
@@ -63,4 +75,50 @@ public class AddConfig
6375
/// </summary>
6476
public required IEnumerable<string> ThingPlaceholders { get; set; }
6577
}
78+
79+
public class StringReplacements
80+
{
81+
public Dictionary<string, string> FileNamePatterns { get; }
82+
public Dictionary<string, string> FileContentPatterns { get; }w
83+
84+
public StringReplacements(AddConfig config)
85+
{
86+
FileNamePatterns = CreateDictionary(config);
87+
FileContentPatterns = CreateDictionary(config);
88+
}
89+
90+
public Dictionary<string, string> CreateDictionary(AddConfig config)
91+
{
92+
var result = config.ThingPlaceholders
93+
.ToDictionary(s => s, s => config.ThingName);
94+
95+
result.TryAdd(Template.Template.AppPlaceholder, config.SolutionName);
96+
return result;
97+
}
98+
99+
100+
public StringReplacements AddFileNamePatterns(Dictionary<string, string> patterns)
101+
{
102+
foreach (var pattern in patterns)
103+
{
104+
if (!FileNamePatterns.ContainsKey(pattern.Key))
105+
{
106+
FileNamePatterns.Add(pattern.Key, pattern.Value);
107+
}
108+
}
109+
return this;
110+
}
111+
112+
public StringReplacements AddContentPatterns(Dictionary<string, string> patterns)
113+
{
114+
foreach (var pattern in patterns)
115+
{
116+
if (!FileContentPatterns.ContainsKey(pattern.Key))
117+
{
118+
FileContentPatterns.Add(pattern.Key, pattern.Value);
119+
}
120+
}
121+
return this;
122+
}
123+
}
66124
}

src/Moryx.Cli.Commands/CommandResult.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
namespace Moryx.Cli.Commands
1+
using Microsoft.CodeAnalysis.CSharp.Syntax;
2+
using Moryx.Tools;
3+
4+
namespace Moryx.Cli.Commands
25
{
36
public class CommandResult
47
{
58
private int ReturnCode { get; set; }
69
public string? Success { get; private set; }
710
public string? Warning { get; private set; }
8-
public string? Error { get; private set; }
11+
public IList<string> Errors { get; private set; } = [];
912

10-
public static CommandResult WithError(string error)
13+
public static CommandResult WithError(string error)
1114
=> new()
1215
{
1316
Success = null,
14-
Error = error,
17+
Errors = [error],
1518
Warning = null,
1619
ReturnCode = 1,
1720
};
@@ -21,20 +24,29 @@ public static CommandResult IsOk(string message, string? warning = null)
2124
{
2225
Success = message,
2326
Warning = warning,
24-
Error = null,
2527
ReturnCode = 0,
2628
};
2729

30+
public CommandResult CouldHaveIssues(IReadOnlyCollection<CommandResult> results)
31+
{
32+
foreach (var result in results)
33+
{
34+
result.OnError(Errors.Add);
35+
}
36+
return this;
37+
}
38+
2839
public CommandResult OnError(Action<string> action)
2940
{
30-
if (Error != null)
31-
action(Error);
41+
if (Errors.Count > 0)
42+
foreach (var error in Errors)
43+
action(error);
3244
return this;
3345
}
3446

3547
public CommandResult OnSuccess(Action<string, string?> action)
3648
{
37-
if(Success != null)
49+
if (Success != null)
3850
action(Success, Warning);
3951
return this;
4052
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Microsoft.Build.Construction;
2+
using Microsoft.CodeAnalysis;
3+
using Moryx.Cli.Template.Models;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Moryx.Cli.Commands.Components
11+
{
12+
internal class SolutionFileManipulation
13+
{
14+
15+
16+
public static void AddProjectToSolution(TemplateSettings settings, string? projectFileName)
17+
{
18+
if (projectFileName == null)
19+
return;
20+
var solutionFileName = Path.Combine(settings.AppName, $"{settings.AppName}.sln");
21+
22+
23+
// It'd be prefered to utilize Roslyn to add the project to the
24+
// solution, but loading the solution/workspace in the first place
25+
// involves some analyzing and takes way too much time for being
26+
// used in a simple CLI command.
27+
28+
// This is 'the' GUID used for `.csproj` inside `.sln`s. Got it from
29+
// `SolutionFile` of `Microsoft.Build.Construction`
30+
const string csharpProjectGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
31+
var guid = Guid.NewGuid().ToString("B").ToUpper();
32+
33+
34+
var filename = Path.GetFileNameWithoutExtension(projectFileName);
35+
var relativePath = Path.GetRelativePath(settings.TargetDirectory, projectFileName);
36+
var str = $"Project(\"{csharpProjectGuid}\") = \"{filename}\", \"{relativePath}\", \"{{{guid}}}\"\nEndProject\n";
37+
38+
var lines = File.ReadAllLines(solutionFileName);
39+
using var fileWriter = new StreamWriter(solutionFileName);
40+
foreach (var line in lines)
41+
{
42+
if (line.StartsWith("Global"))
43+
{
44+
fileWriter.WriteLine(str);
45+
}
46+
fileWriter.WriteLine(line);
47+
}
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)