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),
};
}
}
}