-
|
Hello, |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
|
Implementing This can be implemented using a three-aspect pattern:
Here's an implementation (not compiled or tested): using Metalama.Framework.Aspects;
using Metalama.Framework.Code;
using Metalama.Framework.Eligibility;
using System;
using System.IO;
using System.Linq;
// Marker attribute for directory paths
public class ExistingPathAttribute : Attribute, IAspect<IParameter>
{
public void BuildAspect(IAspectBuilder<IParameter> builder)
{
// Request the implementation aspect on the method
builder.With(builder.Target.DeclaringMember).RequireAspect<PathValidationAspect>();
}
public void BuildEligibility(IEligibilityBuilder<IParameter> builder)
{
builder.MustSatisfy(p => p.Type.Is(typeof(string)),
p => $"Parameter must be of type string");
}
}
// Marker attribute for file paths
public class ExistingFileAttribute : Attribute, IAspect<IParameter>
{
public void BuildAspect(IAspectBuilder<IParameter> builder)
{
// Request the implementation aspect on the method
builder.With(builder.Target.DeclaringMember).RequireAspect<PathValidationAspect>();
}
public void BuildEligibility(IEligibilityBuilder<IParameter> builder)
{
builder.MustSatisfy(p => p.Type.Is(typeof(string)),
p => $"Parameter must be of type string");
}
}
// Implementation aspect
[AspectOrder(AspectOrderDirection.CompileTime, typeof(ExistingPathAttribute), typeof(ExistingFileAttribute), typeof(PathValidationAspect))]
public class PathValidationAspect : OverrideMethodAspect
{
public override dynamic? OverrideMethod()
{
// Find the base path parameter (0 or 1 expected) - compile-time
var basePathParam = meta.Target.Parameters
.Where(p => p.Enhancements().HasAspect<ExistingPathAttribute>())
.SingleOrDefault();
// Get the value at run-time (compile-time if to check existence)
string? basePath = null;
if (basePathParam != null)
{
basePath = (string?)basePathParam.Value;
// Validate the base path
if (basePath != null && !Directory.Exists(basePath))
{
throw new DirectoryNotFoundException($"Directory does not exist: {basePath}");
}
}
// Validate file parameters
foreach (var p in meta.Target.Parameters.Where(p => p.Enhancements().HasAspect<ExistingFileAttribute>()))
{
var filePath = (string?)p.Value;
if (filePath != null)
{
var fullPath = basePath != null ? Path.Combine(basePath, filePath) : filePath;
if (!File.Exists(fullPath))
{
throw new FileNotFoundException($"File does not exist: {fullPath}", fullPath);
}
}
}
return meta.Proceed();
}
}Usage: public class FileProcessor
{
public void ProcessFile([ExistingPath] string directory, [ExistingFile] string fileName)
{
// directory is validated to exist
// fileName is validated relative to directory
var fullPath = Path.Combine(directory, fileName);
// ...
}
public void ProcessAbsoluteFile([ExistingFile] string absolutePath)
{
// absolutePath is validated as an absolute path (no ExistingPath present)
// ...
}
}Key points:
|
Beta Was this translation helpful? Give feedback.
Implementing
[ExistingPath]and[ExistingFile]in Metalama requires understanding how these contracts interact. The key challenge is that[ExistingFile]typically validates a file path relative to a directory marked with[ExistingPath]. But normal contracts in Metalama are self-contained - they cannot reference each other.This can be implemented using a three-aspect pattern:
[ExistingPath]- A marker attribute for directory parameters[ExistingFile]- A marker attribute for file parametersPathValidationAspect- The implementation aspect that performs the actual validationHere's an implementation (not compiled or tested):