@milkmaccya2/hostswitch
Version:
A simple CLI tool to manage and switch between multiple hosts file profiles for different development environments
289 lines • 11.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InteractiveUserInterface = void 0;
const inquirer_1 = __importDefault(require("inquirer"));
class InteractiveUserInterface {
facade;
logger;
constructor(facade, logger) {
this.facade = facade;
this.logger = logger;
}
showMessage(message, type = 'info') {
switch (type) {
case 'info':
this.logger.info(message);
break;
case 'error':
this.logger.error(message);
break;
case 'success':
this.logger.success(message);
break;
case 'warning':
this.logger.warning(message);
break;
}
}
async promptConfirm(message) {
const answer = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'confirmed',
message,
default: false,
},
]);
return answer.confirmed;
}
async promptSelect(message, choices) {
const answer = await inquirer_1.default.prompt([
{
type: 'list',
name: 'selected',
message,
choices,
},
]);
return answer.selected;
}
async promptInput(message, validator) {
const answer = await inquirer_1.default.prompt([
{
type: 'input',
name: 'input',
message,
validate: validator,
},
]);
return answer.input;
}
async handleCommandResult(result) {
if (result.requiresSudo) {
await this.handleSudoRequired(result);
return;
}
if (result.requiresConfirmation) {
const confirmed = await this.promptConfirm(`Are you sure? ${result.message || ''}`);
if (!confirmed) {
this.showMessage('Operation cancelled', 'info');
return;
}
}
if (result.success) {
if (result.message) {
this.showMessage(result.message, 'success');
}
}
else {
this.showMessage(result.message || 'Operation failed', 'error');
}
}
async run() {
while (true) {
try {
const action = await this.showMainMenu();
if (action === 'exit') {
this.showMessage('Goodbye!', 'info');
break;
}
const shouldExit = await this.executeAction(action);
if (shouldExit) {
break;
}
}
catch (error) {
this.showMessage(`Error: ${error instanceof Error ? error.message : String(error)}`, 'error');
}
}
}
async showMainMenu() {
const currentProfile = this.facade.getCurrentProfile();
const statusText = currentProfile ? `current: ${currentProfile}` : 'no profile active';
const choices = [
{ name: `Switch profile (${statusText})`, value: 'switch' },
{ name: 'List all profiles', value: 'list' },
{ name: 'Create new profile', value: 'create' },
{ name: 'Edit profile', value: 'edit' },
{ name: 'Show profile content', value: 'show' },
{ name: 'Delete profile', value: 'delete' },
{ name: 'Exit', value: 'exit' },
];
return this.promptSelect('What would you like to do?', choices);
}
async executeAction(action) {
switch (action) {
case 'list':
await this.handleListProfiles();
return false; // Continue interactive mode
case 'switch':
await this.handleSwitchProfile();
return true; // Exit after action
case 'create':
await this.handleCreateProfile();
return true; // Exit after action
case 'edit':
await this.handleEditProfile();
return true; // Exit after action
case 'show':
await this.handleShowProfile();
return true; // Exit after action
case 'delete':
await this.handleDeleteProfile();
return true; // Exit after action
default:
return false;
}
}
async handleListProfiles() {
const result = await this.facade.listProfiles();
if (result.success && result.data) {
const data = result.data;
const profiles = data.profiles;
if (profiles.length === 0) {
this.showMessage('No profiles found. Create one first!', 'info');
}
else {
this.showMessage('Available profiles:', 'info');
for (const profile of profiles) {
const status = profile.isActive ? ' (current)' : '';
this.showMessage(` ${profile.name}${status}`, 'info');
}
}
}
else {
await this.handleCommandResult(result);
}
}
async handleSwitchProfile() {
const listResult = await this.facade.listProfiles();
if (!listResult.success || !listResult.data) {
this.showMessage('No profiles available to switch to', 'warning');
return;
}
const listData = listResult.data;
if (listData.profiles.length === 0) {
this.showMessage('No profiles available to switch to', 'warning');
return;
}
const switchableProfiles = listData.profiles.filter((p) => !p.isActive);
if (switchableProfiles.length === 0) {
this.showMessage('No other profiles available to switch to', 'info');
return;
}
const choices = switchableProfiles.map((p) => ({
name: p.name,
value: p.name,
}));
const profileName = await this.promptSelect('Select profile to switch to:', choices);
const result = await this.facade.switchProfile(profileName);
await this.handleCommandResult(result);
}
async handleCreateProfile() {
const profileName = await this.promptInput('Enter profile name:', (input) => {
if (!input.trim())
return 'Profile name cannot be empty';
if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
return 'Use only letters, numbers, hyphens, and underscores';
}
return true;
});
const fromCurrent = await this.promptConfirm('Copy current hosts file content?');
const result = await this.facade.createProfile(profileName, fromCurrent);
await this.handleCommandResult(result);
}
async handleEditProfile() {
const listResult = await this.facade.listProfiles();
if (!listResult.success || !listResult.data) {
this.showMessage('No profiles available to edit', 'warning');
return;
}
const listData = listResult.data;
if (listData.profiles.length === 0) {
this.showMessage('No profiles available to edit', 'warning');
return;
}
const choices = listData.profiles.map((p) => ({
name: p.name,
value: p.name,
}));
const profileName = await this.promptSelect('Select profile to edit:', choices);
const result = await this.facade.editProfile(profileName);
await this.handleCommandResult(result);
}
async handleShowProfile() {
const listResult = await this.facade.listProfiles();
if (!listResult.success || !listResult.data) {
this.showMessage('No profiles available to show', 'warning');
return;
}
const listData = listResult.data;
if (listData.profiles.length === 0) {
this.showMessage('No profiles available to show', 'warning');
return;
}
const choices = listData.profiles.map((p) => ({
name: p.name,
value: p.name,
}));
const profileName = await this.promptSelect('Select profile to show:', choices);
const result = await this.facade.showProfile(profileName);
if (result.success && result.data) {
const data = result.data;
this.showMessage(`\nContent of profile "${profileName}":`, 'info');
this.showMessage(data.content, 'info');
}
else {
await this.handleCommandResult(result);
}
}
async handleDeleteProfile() {
const deletableProfiles = this.facade.getDeletableProfiles();
if (deletableProfiles.length === 0) {
this.showMessage('No profiles available for deletion', 'warning');
return;
}
const choices = deletableProfiles.map((p) => ({
name: p.name,
value: p.name,
}));
const profileName = await this.promptSelect('Select profile to delete:', choices);
const confirmed = await this.promptConfirm(`Are you sure you want to delete "${profileName}"?`);
if (confirmed) {
const result = await this.facade.deleteProfile(profileName);
if (result.success) {
this.showMessage(result.message || 'Profile deleted successfully', 'success');
}
else {
await this.handleCommandResult(result);
}
}
else {
this.showMessage('Deletion cancelled', 'info');
}
}
async handleSudoRequired(result) {
this.showMessage('This operation requires sudo privileges.', 'warning');
if (result.sudoCommand?.includes('switch')) {
// sudo hostswitch switch profile-name の形式から profile-name を抽出
const parts = result.sudoCommand.split(' ');
const switchIndex = parts.indexOf('switch');
if (switchIndex >= 0 && switchIndex + 1 < parts.length) {
const profileName = parts[switchIndex + 1];
this.showMessage(`Switching to profile "${profileName}" with sudo...`, 'info');
try {
const sudoResult = await this.facade.switchProfileWithSudo(profileName);
await this.handleCommandResult(sudoResult);
}
catch (error) {
this.showMessage(`Failed to execute sudo command: ${error instanceof Error ? error.message : String(error)}`, 'error');
}
}
}
}
}
exports.InteractiveUserInterface = InteractiveUserInterface;
//# sourceMappingURL=InteractiveUserInterface.js.map