package providers import ( "fmt" "strings" "chorus/pkg/repository" ) // ProviderFactory creates task providers for different repository types type ProviderFactory struct { supportedProviders map[string]ProviderCreator } // ProviderCreator is a function that creates a provider from config type ProviderCreator func(config *repository.Config) (repository.TaskProvider, error) // NewProviderFactory creates a new provider factory with all supported providers func NewProviderFactory() *ProviderFactory { factory := &ProviderFactory{ supportedProviders: make(map[string]ProviderCreator), } // Register all supported providers factory.RegisterProvider("gitea", func(config *repository.Config) (repository.TaskProvider, error) { return NewGiteaProvider(config) }) factory.RegisterProvider("github", func(config *repository.Config) (repository.TaskProvider, error) { return NewGitHubProvider(config) }) factory.RegisterProvider("gitlab", func(config *repository.Config) (repository.TaskProvider, error) { return NewGitLabProvider(config) }) factory.RegisterProvider("mock", func(config *repository.Config) (repository.TaskProvider, error) { return &repository.MockTaskProvider{}, nil }) return factory } // RegisterProvider registers a new provider creator func (f *ProviderFactory) RegisterProvider(providerType string, creator ProviderCreator) { f.supportedProviders[strings.ToLower(providerType)] = creator } // CreateProvider creates a task provider based on the configuration func (f *ProviderFactory) CreateProvider(ctx interface{}, config *repository.Config) (repository.TaskProvider, error) { if config == nil { return nil, fmt.Errorf("configuration cannot be nil") } providerType := strings.ToLower(config.Provider) if providerType == "" { // Fall back to Type field if Provider is not set providerType = strings.ToLower(config.Type) } if providerType == "" { return nil, fmt.Errorf("provider type must be specified in config.Provider or config.Type") } creator, exists := f.supportedProviders[providerType] if !exists { return nil, fmt.Errorf("unsupported provider type: %s. Supported types: %v", providerType, f.GetSupportedTypes()) } provider, err := creator(config) if err != nil { return nil, fmt.Errorf("failed to create %s provider: %w", providerType, err) } return provider, nil } // GetSupportedTypes returns a list of all supported provider types func (f *ProviderFactory) GetSupportedTypes() []string { types := make([]string, 0, len(f.supportedProviders)) for providerType := range f.supportedProviders { types = append(types, providerType) } return types } // SupportedProviders returns list of supported providers (alias for GetSupportedTypes) func (f *ProviderFactory) SupportedProviders() []string { return f.GetSupportedTypes() } // ValidateConfig validates a provider configuration func (f *ProviderFactory) ValidateConfig(config *repository.Config) error { if config == nil { return fmt.Errorf("configuration cannot be nil") } providerType := strings.ToLower(config.Provider) if providerType == "" { providerType = strings.ToLower(config.Type) } if providerType == "" { return fmt.Errorf("provider type must be specified") } // Check if provider type is supported if _, exists := f.supportedProviders[providerType]; !exists { return fmt.Errorf("unsupported provider type: %s", providerType) } // Provider-specific validation switch providerType { case "gitea": return f.validateGiteaConfig(config) case "github": return f.validateGitHubConfig(config) case "gitlab": return f.validateGitLabConfig(config) case "mock": return nil // Mock provider doesn't need validation default: return fmt.Errorf("validation not implemented for provider type: %s", providerType) } } // validateGiteaConfig validates Gitea-specific configuration func (f *ProviderFactory) validateGiteaConfig(config *repository.Config) error { if config.BaseURL == "" { return fmt.Errorf("baseURL is required for Gitea provider") } if config.AccessToken == "" { return fmt.Errorf("accessToken is required for Gitea provider") } if config.Owner == "" { return fmt.Errorf("owner is required for Gitea provider") } if config.Repository == "" { return fmt.Errorf("repository is required for Gitea provider") } return nil } // validateGitHubConfig validates GitHub-specific configuration func (f *ProviderFactory) validateGitHubConfig(config *repository.Config) error { if config.AccessToken == "" { return fmt.Errorf("accessToken is required for GitHub provider") } if config.Owner == "" { return fmt.Errorf("owner is required for GitHub provider") } if config.Repository == "" { return fmt.Errorf("repository is required for GitHub provider") } return nil } // validateGitLabConfig validates GitLab-specific configuration func (f *ProviderFactory) validateGitLabConfig(config *repository.Config) error { if config.AccessToken == "" { return fmt.Errorf("accessToken is required for GitLab provider") } // GitLab requires either owner/repository or project_id in settings if config.Owner != "" && config.Repository != "" { return nil // owner/repo provided } if config.Settings != nil { if projectID, ok := config.Settings["project_id"].(string); ok && projectID != "" { return nil // project_id provided } } return fmt.Errorf("either owner/repository or project_id in settings is required for GitLab provider") } // GetProviderInfo returns information about a specific provider func (f *ProviderFactory) GetProviderInfo(providerType string) (*ProviderInfo, error) { providerType = strings.ToLower(providerType) if _, exists := f.supportedProviders[providerType]; !exists { return nil, fmt.Errorf("unsupported provider type: %s", providerType) } switch providerType { case "gitea": return &ProviderInfo{ Name: "Gitea", Type: "gitea", Description: "Gitea self-hosted Git service provider", RequiredFields: []string{"baseURL", "accessToken", "owner", "repository"}, OptionalFields: []string{"taskLabel", "inProgressLabel", "completedLabel", "baseBranch", "branchPrefix"}, SupportedFeatures: []string{"issues", "labels", "comments", "assignments"}, APIDocumentation: "https://docs.gitea.io/en-us/api-usage/", }, nil case "github": return &ProviderInfo{ Name: "GitHub", Type: "github", Description: "GitHub cloud and enterprise Git service provider", RequiredFields: []string{"accessToken", "owner", "repository"}, OptionalFields: []string{"taskLabel", "inProgressLabel", "completedLabel", "baseBranch", "branchPrefix"}, SupportedFeatures: []string{"issues", "labels", "comments", "assignments", "projects"}, APIDocumentation: "https://docs.github.com/en/rest", }, nil case "gitlab": return &ProviderInfo{ Name: "GitLab", Type: "gitlab", Description: "GitLab cloud and self-hosted Git service provider", RequiredFields: []string{"accessToken", "owner/repository OR project_id"}, OptionalFields: []string{"baseURL", "taskLabel", "inProgressLabel", "completedLabel", "baseBranch", "branchPrefix"}, SupportedFeatures: []string{"issues", "labels", "notes", "assignments", "time_tracking", "milestones"}, APIDocumentation: "https://docs.gitlab.com/ee/api/", }, nil case "mock": return &ProviderInfo{ Name: "Mock Provider", Type: "mock", Description: "Mock provider for testing and development", RequiredFields: []string{}, OptionalFields: []string{}, SupportedFeatures: []string{"basic_operations"}, APIDocumentation: "Built-in mock for testing purposes", }, nil default: return nil, fmt.Errorf("provider info not available for: %s", providerType) } } // ProviderInfo contains metadata about a provider type ProviderInfo struct { Name string `json:"name"` Type string `json:"type"` Description string `json:"description"` RequiredFields []string `json:"required_fields"` OptionalFields []string `json:"optional_fields"` SupportedFeatures []string `json:"supported_features"` APIDocumentation string `json:"api_documentation"` } // ListProviders returns detailed information about all supported providers func (f *ProviderFactory) ListProviders() ([]*ProviderInfo, error) { providers := make([]*ProviderInfo, 0, len(f.supportedProviders)) for providerType := range f.supportedProviders { info, err := f.GetProviderInfo(providerType) if err != nil { continue // Skip providers without info } providers = append(providers, info) } return providers, nil }