Result Pattern
GenHub uses a consistent Result pattern for handling operations that may succeed or fail. This pattern provides a standardized way to return data and error information from methods.
Overview
The Result pattern in GenHub consists of several key components:
ResultBase
: The base class for all result typesOperationResult<T>
: Generic result for operations that return data- Specific result types for different domains
ResultBase
ResultBase
is the foundation of the result pattern. It provides common properties for success/failure status, errors, and timing information.
Properties
Success
: Indicates if the operation was successfulFailed
: Indicates if the operation failed (opposite of Success)HasErrors
: Indicates if there are any errorsErrors
: Read-only list of error messagesFirstError
: The first error message, or null if no errorsAllErrors
: All error messages joined into a single stringElapsed
: Time taken for the operationCompletedAt
: Timestamp when the operation completed
Constructors
// Success with no errors
protected ResultBase(bool success, IEnumerable<string>? errors = null, TimeSpan elapsed = default)
// Success/failure with single error
protected ResultBase(bool success, string? error = null, TimeSpan elapsed = default)
OperationResult<T>
OperationResult<T>
extends ResultBase
and adds support for returning data from operations.
Properties
Data
: The data returned by the operation (nullable)FirstError
: The first error message, or null if no errors
Factory Methods
// Create successful result
OperationResult<T> CreateSuccess(T data, TimeSpan elapsed = default)
// Create failed result with single error
OperationResult<T> CreateFailure(string error, TimeSpan elapsed = default)
// Create failed result with multiple errors
OperationResult<T> CreateFailure(IEnumerable<string> errors, TimeSpan elapsed = default)
// Create failed result copying errors from another result
OperationResult<T> CreateFailure(ResultBase result, TimeSpan elapsed = default)
Specific Result Types
GenHub includes several specialized result types for different domains:
LaunchResult
Result of a game launch operation.
Properties:
ProcessId
: The launched process IDException
: Exception that occurred during launchStartTime
: When the launch startedLaunchDuration
: How long the launch tookFirstError
: First error message
Factory Methods:
LaunchResult CreateSuccess(int processId, DateTime startTime, TimeSpan launchDuration)
LaunchResult CreateFailure(string errorMessage, Exception? exception = null)
ValidationResult
Result of a validation operation.
Properties:
ValidatedTargetId
: ID of the validated targetIssues
: List of validation issuesIsValid
: Whether validation passedCriticalIssueCount
: Number of critical issuesWarningIssueCount
: Number of warning issuesInfoIssueCount
: Number of informational issues
UpdateCheckResult
Result of an update check operation.
Properties:
IsUpdateAvailable
: Whether an update is availableCurrentVersion
: Current application versionLatestVersion
: Latest available versionUpdateUrl
: URL for the updateReleaseNotes
: Release notesReleaseTitle
: Release titleErrorMessages
: List of error messagesAssets
: Release assetsHasErrors
: Whether there are errors
Factory Methods:
UpdateCheckResult NoUpdateAvailable()
UpdateCheckResult UpdateAvailable(GitHubRelease release)
UpdateCheckResult Error(string errorMessage)
DetectionResult<T>
Generic result for detection operations.
Properties:
Items
: Detected items
Factory Methods:
DetectionResult<T> Succeeded(IEnumerable<T> items, TimeSpan elapsed)
DetectionResult<T> Failed(string error)
DownloadResult
Result of a file download operation.
Properties:
FilePath
: Path to the downloaded fileBytesDownloaded
: Number of bytes downloadedHashVerified
: Whether hash verification passedAverageSpeedBytesPerSecond
: Download speedFormattedBytesDownloaded
: Formatted bytes (e.g., "1.2 MB")FormattedSpeed
: Formatted speed (e.g., "1.2 MB/s")FirstError
: First error message
Factory Methods:
DownloadResult CreateSuccess(string filePath, long bytesDownloaded, TimeSpan elapsed, bool hashVerified = false)
GitHubUrlParseResult
Result of parsing GitHub repository URLs.
Properties:
Owner
: Repository ownerRepo
: Repository nameTag
: Release tag
Factory Methods:
GitHubUrlParseResult CreateSuccess(string owner, string repo, string? tag)
GitHubUrlParseResult CreateFailure(params string[] errors)
CAS Results
CasGarbageCollectionResult
Result of CAS garbage collection.
Properties:
ObjectsDeleted
: Number of objects deletedBytesFreed
: Bytes freedObjectsScanned
: Objects scannedObjectsReferenced
: Objects keptPercentageFreed
: Percentage of storage freed
CasValidationResult
Result of CAS integrity validation.
Properties:
Issues
: Validation issuesIsValid
: Whether validation passedObjectsValidated
: Objects validatedObjectsWithIssues
: Objects with issues
Usage Examples
Basic Operation Result
public OperationResult<User> GetUserById(int id)
{
try
{
var user = _userRepository.GetById(id);
if (user == null)
{
return OperationResult<User>.CreateFailure("User not found");
}
return OperationResult<User>.CreateSuccess(user);
}
catch (Exception ex)
{
return OperationResult<User>.CreateFailure($"Database error: {ex.Message}");
}
}
Validation Result
public ValidationResult ValidateGameInstallation(string path)
{
var issues = new List<ValidationIssue>();
if (!Directory.Exists(path))
{
issues.Add(new ValidationIssue("Installation directory does not exist", ValidationSeverity.Error, path));
}
// More validation logic...
return new ValidationResult(path, issues);
}
Launch Result
public async Task<LaunchResult> LaunchGame(GameProfile profile)
{
try
{
var startTime = DateTime.UtcNow;
var process = Process.Start(profile.ExecutablePath);
if (process == null)
{
return LaunchResult.CreateFailure("Failed to start process");
}
var launchDuration = DateTime.UtcNow - startTime;
return LaunchResult.CreateSuccess(process.Id, startTime, launchDuration);
}
catch (Exception ex)
{
return LaunchResult.CreateFailure("Launch failed", ex);
}
}
Best Practices
Always check Success/Failed: Before accessing Data or other properties, check if the operation succeeded.
Use appropriate result types: Choose the most specific result type for your operation.
Provide meaningful errors: Include descriptive error messages that help users understand what went wrong.
Include timing information: Pass elapsed time when available for performance monitoring.
Handle exceptions: Convert exceptions to appropriate result failures.
Test thoroughly: Ensure all success and failure paths are tested.
Testing
All result types include comprehensive unit tests covering:
- Constructor behavior
- Property access
- Factory method functionality
- Edge cases and error conditions
See the test files in GenHub.Tests.Core
for examples.