using System; namespace io.github.hatayama.uLoopMCP { /// /// Provides centralized configuration management for different MCP editors using the Visitor pattern. /// /// This class eliminates switch statements scattered across the codebase by centralizing /// all editor-specific configuration logic in one place. When adding a new editor type, /// you only need to: /// 1. Add the new enum value to McpEditorType /// 2. Add corresponding Visit method to IEditorConfigVisitor interface /// 3. Implement the new Visit method in this class /// /// Related classes: /// - McpEditorType: Enum that defines supported editor types /// - EditorConfig: Value object that stores configuration information /// - IEditorConfigVisitor: Visitor interface for type-safe operations /// - UnityMcpPathResolver: Path resolution methods (to be refactored to use this class) /// - McpConstants: Client name constants (to be refactored to use this class) /// public static class EditorConfigProvider { /// /// Gets the configuration for the specified editor type using the Visitor pattern. /// /// The editor type to get configuration for /// EditorConfig containing client name, config path, and config directory /// Thrown when the editor type is not supported public static EditorConfig GetConfig(McpEditorType editorType) { var visitor = new ConfigurationVisitor(); return editorType.Accept(visitor); } /// /// Gets the client name for the specified editor type. /// /// The editor type to get client name for /// The client name string public static string GetClientName(McpEditorType editorType) { return GetConfig(editorType).ClientName; } /// /// Gets the configuration file path for the specified editor type. /// /// The editor type to get config path for /// The absolute path to the configuration file public static string GetConfigPath(McpEditorType editorType) { return GetConfig(editorType).ConfigPath; } /// /// Gets the configuration directory path for the specified editor type. /// /// The editor type to get config directory for /// The absolute path to the configuration directory, or null if not applicable public static string GetConfigDirectory(McpEditorType editorType) { return GetConfig(editorType).ConfigDirectory; } /// /// Concrete implementation of the Visitor pattern for editor configuration. /// private sealed class ConfigurationVisitor : IEditorConfigVisitor { public EditorConfig VisitCursor() { return new EditorConfig( McpConstants.CLIENT_NAME_CURSOR, UnityMcpPathResolver.GetMcpConfigPath(), UnityMcpPathResolver.GetCursorConfigDirectory() ); } public EditorConfig VisitClaudeCode() { return new EditorConfig( McpConstants.CLIENT_NAME_CLAUDE_CODE, UnityMcpPathResolver.GetClaudeCodeConfigPath(), null // Claude Code config is in project root ); } public EditorConfig VisitVSCode() { return new EditorConfig( McpConstants.CLIENT_NAME_VSCODE, UnityMcpPathResolver.GetVSCodeConfigPath(), UnityMcpPathResolver.GetVSCodeConfigDirectory() ); } public EditorConfig VisitGeminiCLI() { return new EditorConfig( McpConstants.CLIENT_NAME_GEMINI_CLI, UnityMcpPathResolver.GetGeminiCLIConfigPath(), UnityMcpPathResolver.GetGeminiConfigDirectory() ); } public EditorConfig VisitWindsurf() { return new EditorConfig( McpConstants.CLIENT_NAME_WINDSURF, UnityMcpPathResolver.GetWindsurfConfigPath(), UnityMcpPathResolver.GetWindsurfConfigDirectory() ); } public EditorConfig VisitCodex() { return new EditorConfig( McpConstants.CLIENT_NAME_CODEX, UnityMcpPathResolver.GetCodexConfigPath(), UnityMcpPathResolver.GetCodexConfigDirectory() ); } public EditorConfig VisitMcpInspector() { return new EditorConfig( McpConstants.CLIENT_NAME_MCP_INSPECTOR, UnityMcpPathResolver.GetMcpInspectorConfigPath(), null // MCP Inspector config is in project root ); } } } /// /// Extension methods for McpEditorType to support the Visitor pattern. /// public static class McpEditorTypeExtensions { /// /// Accepts a visitor and dispatches to the appropriate Visit method based on the editor type. /// This method eliminates the need for switch statements when working with McpEditorType. /// /// The return type of the visitor operation /// The editor type to dispatch /// The visitor to accept /// The result of the visitor operation /// Thrown when the editor type is not supported public static T Accept(this McpEditorType editorType, IEditorConfigVisitor visitor) { return editorType switch { McpEditorType.Cursor => visitor.VisitCursor(), McpEditorType.ClaudeCode => visitor.VisitClaudeCode(), McpEditorType.VSCode => visitor.VisitVSCode(), McpEditorType.GeminiCLI => visitor.VisitGeminiCLI(), McpEditorType.Windsurf => visitor.VisitWindsurf(), McpEditorType.Codex => visitor.VisitCodex(), McpEditorType.McpInspector => visitor.VisitMcpInspector(), _ => throw new ArgumentOutOfRangeException(nameof(editorType), editorType, null), }; } } }