using System.ComponentModel.DataAnnotations; using MarketAlly.GitCommitEditor.Resources; namespace MarketAlly.GitCommitEditor.Options; public sealed class GitImproverOptions : IValidatableObject { /// /// Root directory to scan for git repositories /// public string WorkspaceRoot { get; set; } = string.Empty; /// /// Path to persist state between sessions /// public string StateFilePath { get; set; } = "git-improver-state.json"; /// /// Rules for analyzing commit message quality /// public CommitMessageRules Rules { get; set; } = new(); /// /// AI configuration for generating suggestions /// public AiOptions Ai { get; set; } = new(); /// /// Maximum number of commits to analyze per repo /// public int MaxCommitsPerRepo { get; set; } = 100; /// /// Only analyze commits newer than this date /// public DateTimeOffset? AnalyzeSince { get; set; } /// /// Skip commits by these authors (useful for excluding bots) /// public string[] ExcludedAuthors { get; set; } = []; public IEnumerable Validate(ValidationContext validationContext) { if (string.IsNullOrWhiteSpace(WorkspaceRoot)) { yield return new ValidationResult( Str.Validation_WorkspaceRequired, [nameof(WorkspaceRoot)]); } else if (!Directory.Exists(WorkspaceRoot)) { yield return new ValidationResult( Str.Validation_WorkspaceNotFound(WorkspaceRoot), [nameof(WorkspaceRoot)]); } if (MaxCommitsPerRepo <= 0) { yield return new ValidationResult( Str.Validation_MaxCommitsPositive, [nameof(MaxCommitsPerRepo)]); } if (Rules == null) { yield return new ValidationResult( Str.Validation_RulesNull, [nameof(Rules)]); } if (Ai == null) { yield return new ValidationResult( Str.Validation_AiOptionsNull, [nameof(Ai)]); } } public void ValidateAndThrow() { var results = Validate(new ValidationContext(this)).ToList(); if (results.Count > 0) { var messages = string.Join("; ", results.Select(r => r.ErrorMessage)); throw new ValidationException(Str.Validation_InvalidOptions(messages)); } } }