Skip to content

Commit 4fa945c

Browse files
committed
Fix and update creation of states
States are considered to be added for a certain context, usually a resource. When a context is found (as provided by `moryx add states <CONTEXT>`), all provided states and a `<Context>StateBase` will be created inside a `States` folder next to the file which contains the given context.
1 parent 4f9389d commit 4fa945c

File tree

22 files changed

+417
-135
lines changed

22 files changed

+417
-135
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,31 @@ Adds a product 'name'
2424

2525
moryx add product <NAME>
2626

27+
28+
## `add state`
29+
30+
Adds a 'state machine' to a specified context. It adds a folder `States` next
31+
to the file that contains the context including a `StateBase` and `State` files
32+
for each provided state. The context will be updated to implement `IStateContext`.
33+
34+
Adding further states later is possible and would add the new states and update
35+
the existing `StateBase` accordingly.
36+
37+
_Note_: Adding states to the `States` folder may lead conflicts if you try to
38+
create states for several contexts that live inside the same path. We recommend
39+
to move those 'to be contexts' to separate directories or libraries before
40+
adding state machines in order to maintain a clean project structure.
41+
42+
43+
### Usage
44+
45+
moryx add states <CONTEXT> --states <Comma separated list of states>
46+
47+
Example
48+
49+
moryx add states AssemblingCell --states "Initialing, Running, Testing"
50+
51+
2752

2853
## `add step`
2954

src/Moryx.Cli.Commands/AddResources.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ public static CommandResult Exec(TemplateSettings settings, IEnumerable<string>
1717
ThingName = $"{resource}Resource",
1818
Thing = "resource",
1919
ThingPlaceholders =
20-
[
21-
Template.Template.ResourcePlaceholder,
20+
[
21+
Template.Template.ResourcePlaceholder,
2222
Template.Template.ResourcePlaceholder2
23-
],
23+
],
2424
};
2525
return AddThing.Exec(
2626
settings,

src/Moryx.Cli.Commands/AddStates.cs

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using Moryx.Cli.Template.StateBaseTemplate;
55
using Moryx.Cli.Template.Exceptions;
66
using Moryx.Cli.Template.StateTemplate;
7+
using Moryx.Cli.Template.Components;
8+
using Moryx.AbstractionLayer.Resources;
79

810
namespace Moryx.Cli.Commands
911
{
@@ -19,16 +21,17 @@ internal static CommandResult Exec(TemplateSettings settings, string resource, I
1921

2022
private static CommandResult Add(TemplateSettings settings, List<string> cleanedResourceNames, string resource, IEnumerable<string> states)
2123
{
22-
if (!ResourceExists(settings, resource))
24+
var resourceFile = FindResource(settings, resource);
25+
if (string.IsNullOrEmpty(resourceFile))
2326
{
24-
return CommandResult.WithError($"Resource `{resource}` not found. Make sure that a `{resource}.cs` exists in the project.");
27+
return CommandResult.WithError($"`{resource}` not found. Make sure that a type `{resource}` exists in the project.");
2528
}
2629

2730
var projectFileNames = cleanedResourceNames.InitialProjects();
28-
var StateBaseFileName = cleanedResourceNames.StateBaseFile();
31+
var stateBaseFileName = cleanedResourceNames.StateBaseFile();
2932

30-
var dictionary = Template.Template.PrepareFileStructure(settings.AppName, StateBaseFileName, projectFileNames);
31-
var targetPath = Path.Combine(settings.TargetDirectory, "src", $"{settings.AppName}.Resources", resource);
33+
var dictionary = Template.Template.PrepareFileStructure(settings.AppName, stateBaseFileName, projectFileNames);
34+
var targetPath = Path.Combine(Path.GetDirectoryName(resourceFile)!, "States");
3235
var newStateBaseFileName = Path.Combine(targetPath, $"{resource}StateBase.cs");
3336

3437
if (!File.Exists(newStateBaseFileName))
@@ -44,13 +47,14 @@ private static CommandResult Add(TemplateSettings settings, List<string> cleaned
4447
{
4548
{ Template.Template.AppPlaceholder, settings.AppName },
4649
{ Template.Template.StateBasePlaceholder, $"{resource}StateBase" },
50+
{ Template.Template.CellPlaceholder, $"{resource}" },
4751
{ Template.Template.ResourcePlaceholder, resource },
4852
});
4953

50-
if(!states.Any())
54+
if (!states.Any())
5155
{
52-
states =
53-
[
56+
states =
57+
[
5458
"Idle",
5559
"ReadyToWork",
5660
"Running",
@@ -92,6 +96,7 @@ private static CommandResult Add(TemplateSettings settings, List<string> cleaned
9296
{ Template.Template.AppPlaceholder, settings.AppName },
9397
{ Template.Template.StatePlaceholder, stateType },
9498
{ Template.Template.ResourcePlaceholder, resource },
99+
{ Template.Template.CellPlaceholder, resource },
95100
{ Template.Template.StateBasePlaceholder, $"{resource}StateBase" },
96101
});
97102

@@ -114,7 +119,7 @@ private static CommandResult Add(TemplateSettings settings, List<string> cleaned
114119

115120
UpdateResource(
116121
settings,
117-
resource,
122+
resource,
118123
success => msg.Add(success),
119124
warning => warnings.Add(warning));
120125

@@ -123,51 +128,69 @@ private static CommandResult Add(TemplateSettings settings, List<string> cleaned
123128

124129
private static void UpdateResource(TemplateSettings settings, string resource, Action<string> onSuccess, Action<string> onWarning)
125130
{
126-
var dir = Path.Combine(settings.TargetDirectory, "src", $"{settings.AppName}.Resources");
127-
var files = Directory.GetFiles(
128-
dir,
129-
$"{resource}.cs",
130-
new EnumerationOptions
131-
{
132-
ReturnSpecialDirectories = false,
133-
RecurseSubdirectories = true
134-
});
135-
if(files.Length == 0)
136-
{
137-
onWarning($"Filename `{resource}.cs` not found. Could not update resource.");
138-
return;
139-
}
140-
if (files.Length > 1)
131+
var candidate = FindResource(settings, resource);
132+
133+
if (string.IsNullOrEmpty(candidate))
141134
{
142-
onWarning($"Filename `{resource}.cs` is ambiguous. Could not update resource.");
135+
onWarning($"Type `{resource}` not found. Could not update resource.");
143136
return;
144137
}
145-
var filename = files.Single();
146138

147139
try
148140
{
149-
var template = StateTemplate.FromFile(filename);
150-
template = template.ImplementIStateContext(resource);
151-
template.SaveToFile(filename);
152-
onSuccess($"Updated `{resource}.cs`");
141+
var template = StateTemplate.FromFile(candidate);
142+
template = template.ImplementIStateContext(resource);
143+
template.SaveToFile(candidate);
144+
onSuccess($"Updated `{Path.GetFileName(candidate)}`");
153145
}
154146
catch (Exception)
155147
{
156-
onWarning($"Failed to update `{resource}.cs`");
148+
onWarning($"Failed to update `{Path.GetFileName(candidate)}`");
157149
}
158150
}
159151

160-
private static bool ResourceExists(TemplateSettings settings, string resource)
152+
private static string FindResource(TemplateSettings settings, string resourceName)
161153
{
154+
var dir = Path.Combine(settings.TargetDirectory, "src");
162155
var files = Directory.GetFiles(
163-
Path.Combine(settings.TargetDirectory),
164-
$"{resource}.cs",
156+
dir,
157+
$"*.cs",
165158
new EnumerationOptions
166159
{
167160
ReturnSpecialDirectories = false,
168161
RecurseSubdirectories = true
169-
});
170-
return files.Length > 0;
162+
})
163+
.AsEnumerable();
164+
165+
var promisingCandidate = files.FirstOrDefault(f => f.EndsWith($"{resourceName}.cs"));
166+
if (!string.IsNullOrEmpty(promisingCandidate))
167+
{
168+
files = files.Prepend(promisingCandidate);
169+
}
170+
171+
var candidate = string.Empty;
172+
foreach (var file in files)
173+
{
174+
if (ContainsType(file, resourceName))
175+
{
176+
candidate = file;
177+
break;
178+
}
179+
}
180+
return candidate;
181+
}
182+
183+
private static bool ContainsType(string filename, string typeName)
184+
{
185+
try
186+
{
187+
var file = CSharpFile.FromFile(filename);
188+
return file.Types.Any(t => t == typeName);
189+
}
190+
catch
191+
{
192+
return false;
193+
}
171194
}
172195
}
173196
}

src/Moryx.Cli.Commands/AddThing.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public class AddConfig
7979
public class StringReplacements
8080
{
8181
public Dictionary<string, string> FileNamePatterns { get; }
82-
public Dictionary<string, string> FileContentPatterns { get; }w
82+
public Dictionary<string, string> FileContentPatterns { get; }
8383

8484
public StringReplacements(AddConfig config)
8585
{

src/Moryx.Cli.Commands/Components/SolutionFileManipulation.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
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;
1+
using Moryx.Cli.Template.Models;
92

103
namespace Moryx.Cli.Commands.Components
114
{
@@ -17,7 +10,7 @@ public static void AddProjectToSolution(TemplateSettings settings, string? proje
1710
{
1811
if (projectFileName == null)
1912
return;
20-
var solutionFileName = Path.Combine(settings.AppName, $"{settings.AppName}.sln");
13+
var solutionFileName = Path.Combine($"{settings.AppName}.sln");
2114

2215

2316
// It'd be prefered to utilize Roslyn to add the project to the
@@ -27,13 +20,13 @@ public static void AddProjectToSolution(TemplateSettings settings, string? proje
2720

2821
// This is 'the' GUID used for `.csproj` inside `.sln`s. Got it from
2922
// `SolutionFile` of `Microsoft.Build.Construction`
30-
const string csharpProjectGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
23+
const string csharpProjectGuid = "{9A19103F-16F7-4668-BE54-9A1E7A4F7556}";
3124
var guid = Guid.NewGuid().ToString("B").ToUpper();
3225

3326

3427
var filename = Path.GetFileNameWithoutExtension(projectFileName);
3528
var relativePath = Path.GetRelativePath(settings.TargetDirectory, projectFileName);
36-
var str = $"Project(\"{csharpProjectGuid}\") = \"{filename}\", \"{relativePath}\", \"{{{guid}}}\"\nEndProject\n";
29+
var str = $"Project(\"{csharpProjectGuid}\") = \"{filename}\", \"{relativePath}\", \"{{{guid}}}\"\nEndProject";
3730

3831
var lines = File.ReadAllLines(solutionFileName);
3932
using var fileWriter = new StreamWriter(solutionFileName);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
5+
namespace Moryx.Cli.Template.Components
6+
{
7+
public class CSharpFile : CSharpFileBase
8+
{
9+
private SyntaxNode _root;
10+
11+
private NamespaceDeclarationSyntax? _namespaceDeclaration;
12+
13+
public List<string> Types { get; private set; }
14+
15+
16+
public string NamespaceName
17+
{
18+
get => _namespaceDeclaration?.Name.ToString() ?? "";
19+
set
20+
{
21+
UpdateNamespace(value);
22+
}
23+
}
24+
25+
26+
public CSharpFile(string content) : base(content)
27+
{
28+
_root = _syntaxTree.GetRoot();
29+
Types = ScanTypes();
30+
_namespaceDeclaration = ScanNamespace();
31+
}
32+
33+
public static CSharpFile FromFile(string fileName)
34+
{
35+
if (!File.Exists(fileName))
36+
{
37+
throw new FileNotFoundException(fileName);
38+
}
39+
var content = File.ReadAllText(fileName);
40+
return new CSharpFile(content);
41+
}
42+
43+
public List<string> ScanTypes()
44+
{
45+
return _root
46+
.DescendantNodes()
47+
.OfType<ClassDeclarationSyntax>()
48+
.Select(n => n.Identifier.Text)
49+
.ToList();
50+
}
51+
52+
private NamespaceDeclarationSyntax? ScanNamespace()
53+
{
54+
var root = _syntaxTree.GetRoot() as CompilationUnitSyntax;
55+
56+
return root?.DescendantNodes().OfType<NamespaceDeclarationSyntax>().FirstOrDefault();
57+
}
58+
59+
private void UpdateNamespace(string @namespace)
60+
{
61+
if (_namespaceDeclaration != null)
62+
{
63+
var newNamespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(@namespace)).NormalizeWhitespace();
64+
_root = _root.ReplaceNode(_namespaceDeclaration, newNamespace);
65+
_content = _root.ToFullString();
66+
}
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)