|
|
|
@ -54,7 +54,7 @@ public class ModuleManager |
|
|
|
|
|
|
|
|
|
foreach (var nested in type.GetNestedTypes()) { |
|
|
|
|
if (!nested.GetCustomAttributes(true).OfType<ICreateEntityAttribute>().Any()) continue; |
|
|
|
|
var name = nested.Get<EntityAttribute>()?.Name ?? nested.Name; |
|
|
|
|
var name = nested.Get<EntityAttribute>()?.Path?.Single() ?? nested.Name; |
|
|
|
|
Universe.LookupOrThrow(entity, name).CreateLookup(nested); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -97,22 +97,22 @@ public class ModuleManager |
|
|
|
|
if (attr == null) throw new ArgumentException( |
|
|
|
|
$"Module {type} must be marked with ModuleAttribute", nameof(type)); |
|
|
|
|
|
|
|
|
|
// If path is not specified in the attribute, return the type's name. |
|
|
|
|
if (attr.Path == null) { |
|
|
|
|
var assemblyName = type.Assembly.GetName().Name!; |
|
|
|
|
// If module is static, its path will be implictly global. |
|
|
|
|
var global = (type.IsAbstract && type.IsSealed) || attr.Global; |
|
|
|
|
|
|
|
|
|
if (!type.FullName!.StartsWith(assemblyName + '.')) throw new InvalidOperationException( |
|
|
|
|
$"Module {type} must be defined under namespace {assemblyName}"); |
|
|
|
|
var fullNameWithoutAssembly = type.FullName![(assemblyName.Length + 1)..]; |
|
|
|
|
// If global or path are specified in the attribute, return an absolute path. |
|
|
|
|
if (global || attr.Path != null) |
|
|
|
|
return new(global, attr.Path ?? new[] { type.Name }); |
|
|
|
|
|
|
|
|
|
var parts = fullNameWithoutAssembly.Split('.'); |
|
|
|
|
return new(true, parts.Prepend(assemblyName).ToArray()); |
|
|
|
|
} |
|
|
|
|
// Otherwise, create it based on the type's assembly, namespace and name. |
|
|
|
|
var assemblyName = type.Assembly.GetName().Name!; |
|
|
|
|
|
|
|
|
|
if (!type.FullName!.StartsWith(assemblyName + '.')) throw new InvalidOperationException( |
|
|
|
|
$"Module {type} must be defined under namespace {assemblyName}"); |
|
|
|
|
var fullNameWithoutAssembly = type.FullName![(assemblyName.Length + 1)..]; |
|
|
|
|
|
|
|
|
|
var fullPath = new EntityPath(true, attr.Path); |
|
|
|
|
if (!fullPath.IsAbsolute) throw new ArgumentException( |
|
|
|
|
$"Module {type} must have an absolute path (if specified)", nameof(type)); |
|
|
|
|
return fullPath; |
|
|
|
|
var parts = fullNameWithoutAssembly.Split('.'); |
|
|
|
|
return new(true, parts.Prepend(assemblyName).ToArray()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -182,13 +182,12 @@ internal class ModuleInfo |
|
|
|
|
throw new Exception($"Type {typeHint} must be an empty, used-defined struct."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var name = type.Get<EntityAttribute>()?.Name ?? proxyType.Name; |
|
|
|
|
try { EntityPath.ValidateName(name); } |
|
|
|
|
catch (Exception ex) { throw new Exception( |
|
|
|
|
$"{type} has invalid entity name '{name}: {ex.Message}'", ex); } |
|
|
|
|
var path = (type.Get<EntityAttribute>() is EntityAttribute entityAttr) |
|
|
|
|
? new EntityPath(entityAttr.Global, entityAttr.Path ?? new[] { proxyType.Name }) |
|
|
|
|
: new EntityPath(false, proxyType.Name); |
|
|
|
|
|
|
|
|
|
var builder = Entity.NewChild(name); |
|
|
|
|
if (!type.Has<PrivateAttribute>()) builder.Symbol(name); |
|
|
|
|
var builder = path.IsAbsolute ? Universe.New(path) : Entity.NewChild(path); |
|
|
|
|
if (!type.Has<PrivateAttribute>()) builder.Symbol(path.Name); |
|
|
|
|
|
|
|
|
|
foreach (var attr in type.GetMultiple<AddEntityAttribute>()) |
|
|
|
|
builder.Add(Universe.LookupOrThrow(attr.Entity)); |
|
|
|
|