import { readFile, writeFile, access } from 'fs/promises';
import { constants } from 'fs';
import { StorageService } from './storageService.js';
import { Unit } from '../types/index.js';
import {
getContentByLines,
getContentByChars,
insertContentAtLine,
insertContentAtChar,
removeContentByLines,
removeContentByChars,
validatePosition
} from '../utils/positionUtils.js';
/**
* Реализация StorageService для файловой системы
*/
export class FileSystemStorage extends StorageService {
private backups: Map<string, string> = new Map();
async readContent(filePath: string): Promise<string> {
try {
return await readFile(filePath, 'utf-8');
} catch (error) {
throw new Error(`Failed to read file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async writeContent(filePath: string, content: string): Promise<void> {
try {
await writeFile(filePath, content, 'utf-8');
} catch (error) {
throw new Error(`Failed to write file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async resourceExists(filePath: string): Promise<boolean> {
try {
await access(filePath, constants.F_OK);
return true;
} catch {
return false;
}
}
async extractContent(filePath: string, position: number, count: number, unit: Unit): Promise<string> {
const content = await this.readContent(filePath);
validatePosition(content, position, unit);
if (unit === 'lines') {
return getContentByLines(content, position, count);
} else {
return getContentByChars(content, position, count);
}
}
async insertContent(
filePath: string,
position: number,
content: string,
replaceCount?: number,
unit: Unit = 'lines'
): Promise<void> {
const existingContent = await this.readContent(filePath);
validatePosition(existingContent, position, unit);
let newContent: string;
if (unit === 'lines') {
newContent = insertContentAtLine(existingContent, position, content, replaceCount);
} else {
newContent = insertContentAtChar(existingContent, position, content, replaceCount);
}
await this.writeContent(filePath, newContent);
}
async extractContentByPattern(filePath: string, startPattern: string, endPattern?: string, lineCount?: number): Promise<string> {
const content = await this.readContent(filePath);
const startIndex = content.indexOf(startPattern);
if (startIndex === -1) {
throw new Error(`Start pattern "${startPattern}" not found in file ${filePath}`);
}
if (endPattern) {
const endIndex = content.indexOf(endPattern, startIndex + startPattern.length);
if (endIndex === -1) {
throw new Error(`End pattern "${endPattern}" not found after start pattern`);
}
return content.slice(startIndex, endIndex + endPattern.length);
} else if (lineCount) {
const lines = content.split('\n');
const startLine = content.slice(0, startIndex).split('\n').length - 1;
const endLine = Math.min(startLine + lineCount, lines.length);
return lines.slice(startLine, endLine).join('\n');
} else {
const lines = content.split('\n');
const startLine = content.slice(0, startIndex).split('\n').length - 1;
return lines[startLine];
}
}
async insertContentByMarker(filePath: string, marker: string, content: string, position: 'before' | 'after' | 'replace', replacePattern?: string): Promise<void> {
const existingContent = await this.readContent(filePath);
const markerIndex = existingContent.indexOf(marker);
if (markerIndex === -1) {
throw new Error(`Marker "${marker}" not found in file ${filePath}`);
}
let newContent: string;
if (position === 'before') {
newContent = existingContent.replace(marker, `${content}\n${marker}`);
} else if (position === 'after') {
newContent = existingContent.replace(marker, `${marker}\n${content}`);
} else if (position === 'replace') {
const targetPattern = replacePattern || marker;
newContent = existingContent.replace(targetPattern, content);
} else {
throw new Error(`Invalid position: ${position}`);
}
await this.writeContent(filePath, newContent);
}
async removeContentByPattern(filePath: string, startPattern: string, endPattern?: string, lineCount?: number): Promise<void> {
const content = await this.readContent(filePath);
const startIndex = content.indexOf(startPattern);
if (startIndex === -1) {
throw new Error(`Start pattern "${startPattern}" not found in file ${filePath}`);
}
let newContent: string;
if (endPattern) {
const endIndex = content.indexOf(endPattern, startIndex + startPattern.length);
if (endIndex === -1) {
throw new Error(`End pattern "${endPattern}" not found after start pattern`);
}
newContent = content.slice(0, startIndex) + content.slice(endIndex + endPattern.length);
} else if (lineCount) {
const lines = content.split('\n');
const startLine = content.slice(0, startIndex).split('\n').length - 1;
const endLine = Math.min(startLine + lineCount, lines.length);
lines.splice(startLine, endLine - startLine);
newContent = lines.join('\n');
} else {
const lines = content.split('\n');
const startLine = content.slice(0, startIndex).split('\n').length - 1;
lines.splice(startLine, 1);
newContent = lines.join('\n');
}
await this.writeContent(filePath, newContent);
}
async removeContent(filePath: string, position: number, count: number, unit: Unit): Promise<void> {
const content = await this.readContent(filePath);
validatePosition(content, position, unit);
let newContent: string;
if (unit === 'lines') {
newContent = removeContentByLines(content, position, count);
} else {
newContent = removeContentByChars(content, position, count);
}
await this.writeContent(filePath, newContent);
}
async createBackup(filePath: string): Promise<string> {
const content = await this.readContent(filePath);
const backupId = `${filePath}_${Date.now()}`;
this.backups.set(backupId, content);
return backupId;
}
async restoreBackup(filePath: string, backupId: string): Promise<void> {
const backupContent = this.backups.get(backupId);
if (!backupContent) {
throw new Error(`Backup ${backupId} not found`);
}
await this.writeContent(filePath, backupContent);
this.backups.delete(backupId);
}
}