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.
ResultBase 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
ResultBase Constructors
// Result with optional 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.
OperationResult Properties
Data: The data returned by the operation (nullable)FirstError: The first error message, or null if no errors
OperationResult 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 with single error and partial data
OperationResult<T> CreateFailure(string error, T data, TimeSpan elapsed)
// Create failed result with multiple errors and partial data
OperationResult<T> CreateFailure(IEnumerable<string> errors, T data, TimeSpan elapsed)
// 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
Constructors:
// Standard constructor
public ValidationResult(string validatedTargetId, IEnumerable<ValidationIssue> issues)Factory Methods:
// Result with no issues
public static ValidationResult CreateSuccess(string validatedTargetId)
// Failure with issues
public static ValidationResult CreateFailure(string validatedTargetId, IEnumerable<ValidationIssue> issues)ContentUpdateCheckResult
Result of a content update check operation.
Properties:
IsUpdateAvailable: Whether an update is availableCurrentVersion: Current content versionLatestVersion: Latest available versionDownloadUrl: URL for the update packageChangelog: Release notes or changelog contentHasErrors: Inherited fromResultBase, indicates whether any errors are presentFirstError: Inherited fromResultBase, provides the first error message, if any
Factory Methods:
ContentUpdateCheckResult.CreateUpdateAvailable(string latestVersion, ...): When an update for existing content is found.ContentUpdateCheckResult.CreateNoUpdateAvailable(string currentVersion, ...): When the current version is up to date.ContentUpdateCheckResult.CreateContentAvailable(string latestVersion, ...): Semantic Difference: Use this when search returns content that is not currently installed but available for first-time acquisition.ContentUpdateCheckResult.CreateFailure(string error, ...): When the update check itself fails.
TIP
Always check result.Success before accessing version properties, as they may be null in failure results.
var result = await updateService.CheckForUpdatesAsync(manifest);
if (result.Success && result.IsUpdateAvailable)
{
// Handle update
}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)
DownloadResult CreateFailure(string errorMessage, long bytesDownloaded = 0, TimeSpan elapsed = default)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
GarbageCollectionStats
Result stats for CAS garbage collection wrapped in OperationResult<GarbageCollectionStats>.
Properties:
ObjectsDeleted: Number of objects deletedBytesFreed: Bytes freedObjectsScanned: Total objects scannedObjectsReferenced: Objects kept (referenced, within the grace period, or retained after a failed deletion)Duration: Elapsed timeSkipped: True if collection was skipped (e.g. concurrent run)InProgress: True if collection was skipped due to lock contention
CasValidationResult
Result of CAS integrity validation.
Properties:
Issues: Validation issuesIsValid: Whether validation passedObjectsValidated: Objects validatedObjectsWithIssues: Objects with issues
Constructors:
CasValidationResult(): Creates a successful validation result with no issues.CasValidationResult(issues, objectsValidated, elapsed): Creates a result with validation issues. Note thatSuccesswill befalseonly if critical issues are present.
CasStats
Summary of CAS system state.
Properties:
TotalObjects: Number of objects in CASTotalBytes: Total disk space consumedLastGcTimestamp: When garbage collection was last runIsGcPending: Whether a cleanup is recommended
Factory Methods:
Create(objectCount, totalSize, spaceSaved, hitRate, recentAccesses)
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);
}
}Content Update Check Result
public async Task<ContentUpdateCheckResult> CheckForUpdatesAsync(ContentManifest manifest)
{
try
{
var latestRelease = await _gitHubService.GetLatestReleaseAsync(manifest.Publisher.Id);
if (latestRelease.Version == manifest.Version)
{
return ContentUpdateCheckResult.CreateNoUpdateAvailable(manifest.Version);
}
return ContentUpdateCheckResult.CreateUpdateAvailable(
latestRelease.Version,
manifest.Version,
manifest.Publisher.Id,
manifest.Publisher.Name,
manifest.Id,
manifest.Name,
latestRelease.ReleaseDate,
latestRelease.DownloadUrl,
latestRelease.Changelog);
}
catch (Exception ex)
{
return ContentUpdateCheckResult.CreateFailure($"Update check failed: {ex.Message}");
}
}Scope: Launch, Detection, and IO
Launch, game detection, downloads, hashing, and file IO are fallible and must return OperationResult<T> or the matching domain result (LaunchResult, DetectionResult<T>, DownloadResult, ValidationResult). Only cooperative cancellation (OperationCanceledException) and argument contract violations (ArgumentNullException, invalid arguments) cross these boundaries as exceptions. Convert IOException, UnauthorizedAccessException, Win32, and timeout failures into result failures.
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.
