Skip to content

Commit 3951229

Browse files
authored
Merge pull request #10 from moorestech/feature/add-isRequired
isRequiredの判定を追加
2 parents 399e439 + bf59c38 commit 3951229

7 files changed

Lines changed: 153 additions & 38 deletions

File tree

.claude/settings.local.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"enableAllProjectMcpServers": false,
3+
"permissions": {
4+
"allow": [
5+
"Bash(find:*)"
6+
]
7+
}
8+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
namespace CommandForgeGenerator.Tests;
2+
3+
public class GenerateTestCode
4+
{
5+
public const string TextCommand = """
6+
#if ENABLE_COMMAND_FORGE_GENERATOR
7+
namespace CommandForgeGenerator.Command
8+
{
9+
public partial class TextCommand : ICommandForgeCommand
10+
{
11+
public const string Type = "text";
12+
public readonly CommandId CommandId;
13+
14+
public readonly string Character;
15+
public readonly string Body;
16+
17+
18+
public static TextCommand Create(int commandId, global::Newtonsoft.Json.Linq.JToken json)
19+
{
20+
21+
var Character = (string)json["character"];
22+
var Body = (string)json["body"];
23+
24+
25+
return new TextCommand(commandId, Character, Body);
26+
}
27+
28+
public TextCommand(int commandId, string Character, string Body)
29+
{
30+
CommandId = (CommandId)commandId;
31+
32+
this.Character = Character;
33+
this.Body = Body;
34+
35+
}
36+
}
37+
}
38+
#endif
39+
""";
40+
}

CommandForgeGenerator.Tests/Test.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.IO;
2+
using System.Linq;
23
using CommandForgeGenerator.Generator.CodeGenerate;
34
using CommandForgeGenerator.Generator.Json;
45
using CommandForgeGenerator.Generator.Semantic;
@@ -64,6 +65,7 @@ public void GenerateTest()
6465
var codeFiles = CodeGenerator.Generate(commandsSchema);
6566

6667
Assert.Equal(16, codeFiles.Count);
68+
Assert.Equal(GenerateTestCode.TextCommand, codeFiles.FirstOrDefault(c => c.FileName == "TextCommand.g.cs").Code);
6769

6870
#region Internal
6971

@@ -84,7 +86,6 @@ string GetSampleYaml()
8486
body:
8587
type: string
8688
multiline: true
87-
required: true
8889
8990
- id: emote
9091
label: エモート

CommandForgeGenerator/CodeGenerate/CodeGenerator.cs

Lines changed: 90 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private static string GeneratePropertiesCode(List<CommandProperty> commandProper
101101
properties.AppendLine();
102102
foreach (var property in commandProperties)
103103
{
104-
var type = GetTypeCode(property.Type);
104+
var type = GetTypeCode(property.Type, property.IsRequired);
105105
properties.AppendLine($"public readonly {type} {property.CodeProperty};");
106106
}
107107

@@ -114,39 +114,82 @@ private static string GenerateCreateMethodTempVariables(List<CommandProperty> co
114114
properties.AppendLine();
115115
foreach (var property in commandProperties)
116116
{
117-
var type = GetTypeCode(property.Type);
118-
if (property.Type is CommandPropertyType.CommandId)
119-
{
120-
properties.AppendLine($"var {property.CodeProperty} = (CommandId)((int)json[\"{property.Name}\"]);");
121-
}
122-
else if (property.Type is CommandPropertyType.Vector2)
123-
{
124-
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
125-
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector2((float){property.CodeProperty}Array[0], (float){property.CodeProperty}Array[1]);");
126-
}
127-
else if (property.Type is CommandPropertyType.Vector3)
128-
{
129-
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
130-
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector3((float){property.CodeProperty}Array[0], (float){property.CodeProperty}Array[1], (float){property.CodeProperty}Array[2]);");
131-
}
132-
else if (property.Type is CommandPropertyType.Vector4)
133-
{
134-
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
135-
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector4((float){property.CodeProperty}Array[0], (float){property.CodeProperty}Array[1], (float){property.CodeProperty}Array[2], (float){property.CodeProperty}Array[3]);");
136-
}
137-
else if (property.Type is CommandPropertyType.Vector2Int)
138-
{
139-
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
140-
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector2Int((int){property.CodeProperty}Array[0], (int){property.CodeProperty}Array[1]);");
141-
}
142-
else if (property.Type is CommandPropertyType.Vector3Int)
117+
var type = GetTypeCode(property.Type, property.IsRequired);
118+
119+
if (property.IsRequired)
143120
{
144-
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
145-
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector3Int((int){property.CodeProperty}Array[0], (int){property.CodeProperty}Array[1], (int){property.CodeProperty}Array[2]);");
121+
// required の場合は従来通り
122+
if (property.Type is CommandPropertyType.CommandId)
123+
{
124+
properties.AppendLine($"var {property.CodeProperty} = (CommandId)((int)json[\"{property.Name}\"]);");
125+
}
126+
else if (property.Type is CommandPropertyType.Vector2)
127+
{
128+
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
129+
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector2((float){property.CodeProperty}Array[0], (float){property.CodeProperty}Array[1]);");
130+
}
131+
else if (property.Type is CommandPropertyType.Vector3)
132+
{
133+
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
134+
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector3((float){property.CodeProperty}Array[0], (float){property.CodeProperty}Array[1], (float){property.CodeProperty}Array[2]);");
135+
}
136+
else if (property.Type is CommandPropertyType.Vector4)
137+
{
138+
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
139+
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector4((float){property.CodeProperty}Array[0], (float){property.CodeProperty}Array[1], (float){property.CodeProperty}Array[2], (float){property.CodeProperty}Array[3]);");
140+
}
141+
else if (property.Type is CommandPropertyType.Vector2Int)
142+
{
143+
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
144+
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector2Int((int){property.CodeProperty}Array[0], (int){property.CodeProperty}Array[1]);");
145+
}
146+
else if (property.Type is CommandPropertyType.Vector3Int)
147+
{
148+
properties.AppendLine($"var {property.CodeProperty}Array = json[\"{property.Name}\"];");
149+
properties.AppendLine($"var {property.CodeProperty} = new global::UnityEngine.Vector3Int((int){property.CodeProperty}Array[0], (int){property.CodeProperty}Array[1], (int){property.CodeProperty}Array[2]);");
150+
}
151+
else
152+
{
153+
properties.AppendLine($"var {property.CodeProperty} = ({type})json[\"{property.Name}\"];");
154+
}
146155
}
147156
else
148157
{
149-
properties.AppendLine($"var {property.CodeProperty} = ({type})json[\"{property.Name}\"];");
158+
// nullable の場合はnullチェックを追加
159+
properties.AppendLine($"var {property.CodeProperty}Token = json[\"{property.Name}\"];");
160+
161+
if (property.Type is CommandPropertyType.CommandId)
162+
{
163+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : (CommandId?)((int){property.CodeProperty}Token);");
164+
}
165+
else if (property.Type is CommandPropertyType.Vector2)
166+
{
167+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : new global::UnityEngine.Vector2((float){property.CodeProperty}Token[0], (float){property.CodeProperty}Token[1]);");
168+
}
169+
else if (property.Type is CommandPropertyType.Vector3)
170+
{
171+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : new global::UnityEngine.Vector3((float){property.CodeProperty}Token[0], (float){property.CodeProperty}Token[1], (float){property.CodeProperty}Token[2]);");
172+
}
173+
else if (property.Type is CommandPropertyType.Vector4)
174+
{
175+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : new global::UnityEngine.Vector4((float){property.CodeProperty}Token[0], (float){property.CodeProperty}Token[1], (float){property.CodeProperty}Token[2], (float){property.CodeProperty}Token[3]);");
176+
}
177+
else if (property.Type is CommandPropertyType.Vector2Int)
178+
{
179+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : new global::UnityEngine.Vector2Int((int){property.CodeProperty}Token[0], (int){property.CodeProperty}Token[1]);");
180+
}
181+
else if (property.Type is CommandPropertyType.Vector3Int)
182+
{
183+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : new global::UnityEngine.Vector3Int((int){property.CodeProperty}Token[0], (int){property.CodeProperty}Token[1], (int){property.CodeProperty}Token[2]);");
184+
}
185+
else if (property.Type is CommandPropertyType.String)
186+
{
187+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : (string?){property.CodeProperty}Token;");
188+
}
189+
else
190+
{
191+
properties.AppendLine($"{type} {property.CodeProperty} = {property.CodeProperty}Token?.Type == global::Newtonsoft.Json.Linq.JTokenType.Null ? null : ({type}){property.CodeProperty}Token;");
192+
}
150193
}
151194
}
152195

@@ -169,7 +212,7 @@ public static string GenerateConstructorPropertiesCode(List<CommandProperty> com
169212
var properties = new StringBuilder();
170213
foreach (var property in commandProperties)
171214
{
172-
var type = GetTypeCode(property.Type);
215+
var type = GetTypeCode(property.Type, property.IsRequired);
173216
properties.Append($", {type} {property.CodeProperty}");
174217

175218
}
@@ -189,9 +232,9 @@ private static string GenerateConstructSetPropertiesCode(List<CommandProperty> c
189232
return construct.ToString();
190233
}
191234

192-
private static string GetTypeCode(CommandPropertyType type)
235+
private static string GetTypeCode(CommandPropertyType type, bool isRequired)
193236
{
194-
return type switch
237+
var baseType = type switch
195238
{
196239
CommandPropertyType.String => "string",
197240
CommandPropertyType.Int => "int",
@@ -205,6 +248,20 @@ private static string GetTypeCode(CommandPropertyType type)
205248
CommandPropertyType.Vector3Int => "global::UnityEngine.Vector3Int",
206249
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
207250
};
251+
252+
// 値型でrequiredでない場合は nullable にする
253+
if (!isRequired && type != CommandPropertyType.String)
254+
{
255+
return baseType + "?";
256+
}
257+
258+
// 参照型でも明示的に nullable にする(C# 8.0以降)
259+
if (!isRequired && type == CommandPropertyType.String)
260+
{
261+
return baseType + "?";
262+
}
263+
264+
return baseType;
208265
}
209266

210267
public static string GenerateLoaderCode(CommandsSemantics commandsSemantics)

CommandForgeGenerator/Semantic/CommandSemanticsLoader.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,14 @@ CommandsSemantics ParseCommandsSchema(JsonObject root)
9090
_ => throw new Exception($"未知の property type \"{typeStr}\"")
9191
};
9292

93-
properties.Add(new CommandProperty(mappedType, propName));
93+
// required フィールドを読み取る
94+
var isRequired = true;
95+
if (propObj.Nodes.ContainsKey("required") && propObj["required"] is JsonBoolean requiredNode)
96+
{
97+
isRequired = requiredNode.Literal;
98+
}
99+
100+
properties.Add(new CommandProperty(mappedType, propName, isRequired));
94101
}
95102

96103
commands.Add(new CommandSemantics(commandName, properties));

CommandForgeGenerator/Semantic/CommandsSemantics.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ public CommandSemantics(string name, List<CommandProperty> properties)
2828
public class CommandProperty{
2929
public readonly string Name;
3030
public readonly CommandPropertyType Type;
31+
public readonly bool IsRequired;
3132

3233
public string CodeProperty => Name.ToUpper(0);
3334

34-
public CommandProperty(CommandPropertyType type, string name)
35+
public CommandProperty(CommandPropertyType type, string name, bool isRequired)
3536
{
3637
Type = type;
3738
Name = name;
39+
IsRequired = isRequired;
3840
}
3941

4042
}

CommandForgeGenerator/Semantic/ReservedCommandSemantics.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ public static List<CommandSemantics> GetReservedCommand()
88
{
99
var groupStart = new CommandSemantics("group_start", new List<CommandProperty>()
1010
{
11-
new(CommandPropertyType.String, "groupName"),
12-
new(CommandPropertyType.Bool, "isCollapsed"),
11+
new(CommandPropertyType.String, "groupName", true),
12+
new(CommandPropertyType.Bool, "isCollapsed", true),
1313
});
1414
var groupEnd = new CommandSemantics("group_end", new List<CommandProperty>());
1515
return new List<CommandSemantics>(){groupStart, groupEnd};

0 commit comments

Comments
 (0)