Skip to content

Commit

Permalink
Criação da biblioteca Arquivos (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
PvtWendy authored Aug 14, 2024
1 parent 6cd7a28 commit d3a3aa1
Show file tree
Hide file tree
Showing 2 changed files with 397 additions and 0 deletions.
197 changes: 197 additions & 0 deletions fontes/bibliotecas/arquivos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { InterpretadorInterface } from '@designliquido/delegua/interfaces';
import fs from 'fs';
import path from 'path';

const NUMERO_MAXIMO_ARQUIVOS = 10;

const ModoAcesso = {
LEITURA: 0,
ESCRITA: 1,
ACRESCENTAR: 2,
};

const arquivos = new Array(NUMERO_MAXIMO_ARQUIVOS).fill(null);

export async function abrir_arquivo(interpretador: InterpretadorInterface, caminhoArquivo, modoAcesso) {
if (modoAcesso < 0 || modoAcesso > 2) {
throw new Error(`Modo de acesso inválido: ${modoAcesso}`);
}

if (!arquivoAberto(caminhoArquivo)) {
const indice = obterProximoIndiceLivre();
arquivos[indice] = {
caminho: caminhoArquivo,
modoAcesso,
stream:
modoAcesso === ModoAcesso.LEITURA
? fs.createReadStream(caminhoArquivo, 'utf-8')
: fs.createWriteStream(caminhoArquivo, {
flags: modoAcesso === ModoAcesso.ACRESCENTAR ? 'a' : 'w',
encoding: 'utf-8',
}),
leitor: modoAcesso === ModoAcesso.LEITURA ? fs.promises.readFile(caminhoArquivo, 'utf-8') : null,
escritor:
modoAcesso !== ModoAcesso.LEITURA
? fs.promises.writeFile(caminhoArquivo, '', {
flag: modoAcesso === ModoAcesso.ACRESCENTAR ? 'a' : 'w',
})
: null,
fim: false,
};
return indice;
} else {
throw new Error(`O arquivo '${caminhoArquivo}' já está aberto`);
}
}

export async function fechar_arquivo(interpretador: InterpretadorInterface, endereco) {
const arquivo = obterArquivo(endereco);
if (arquivo) {
arquivo.stream.close();
arquivos[endereco] = null;
} else {
throw new Error(`O endereço de memória especificado não aponta para um arquivo`);
}
}

export function fim_arquivo(interpretador: InterpretadorInterface, endereco) {
const arquivo = obterArquivo(endereco);
return arquivo.fim;
}

export async function ler_linha(interpretador: InterpretadorInterface, endereco) {
const arquivo = obterArquivo(endereco);
if (arquivo.modoAcesso !== ModoAcesso.LEITURA) {
throw new Error(`O arquivo '${arquivo.caminho}' está aberto em modo de escrita`);
}
const data = await arquivo.leitor;
return data.split('\n')[0];
}

export async function escrever_linha(interpretador: InterpretadorInterface, linha, endereco) {
const arquivo = obterArquivo(endereco);
if (arquivo.modoAcesso === ModoAcesso.LEITURA) {
throw new Error(`O arquivo '${arquivo.caminho}' está aberto em modo de leitura`);
}
await fs.promises.appendFile(arquivo.caminho, linha + '\n', 'utf-8');
}

export async function substituir_texto(
interpretador: InterpretadorInterface,
endereco,
textoPesquisa,
textoSubstituto,
onlyFirst
) {
const filePath = path.resolve(endereco);
const data = await fs.promises.readFile(filePath, 'utf-8');
const newText = onlyFirst
? data.replace(textoPesquisa, textoSubstituto)
: data.replace(new RegExp(textoPesquisa, 'g'), textoSubstituto);
await fs.promises.writeFile(filePath, newText, 'utf-8');
}

export async function arquivo_existe(interpretador: InterpretadorInterface, caminhoArquivo) {
const filePath = path.resolve(caminhoArquivo);
return fs.promises
.access(filePath, fs.constants.F_OK)
.then(() => true)
.catch(() => false);
}

export async function apagar_arquivo(interpretador: InterpretadorInterface, caminhoArquivo) {
const filePath = path.resolve(caminhoArquivo);
await fs.promises.unlink(filePath);
}

export async function criar_pasta(interpretador: InterpretadorInterface, caminho) {
const dirPath = path.resolve(caminho);
await fs.promises.mkdir(dirPath, { recursive: true });
}

export async function listar_pastas(interpretador: InterpretadorInterface, caminhoPai, vetorPastas) {
const dirPath = path.resolve(caminhoPai);
const items = await fs.promises.readdir(dirPath, { withFileTypes: true });
const pastas = items.filter((item) => item.isDirectory()).map((item) => item.name);

if (pastas.length > vetorPastas.length) {
throw new Error(
`Não foi possível listar as pastas pois o vetor passado é muito pequeno. O diretório escolhido possui ${pastas.length} pastas, mas o vetor passado comporta apenas ${vetorPastas.length} elementos.`
);
}

pastas.forEach((pasta, i) => {
vetorPastas[i] = pasta;
});

for (let i = pastas.length; i < vetorPastas.length; i++) {
vetorPastas[i] = '';
}
}

export async function listar_arquivos(interpretador: InterpretadorInterface, caminhoPai, vetorArquivos) {
const dirPath = path.resolve(caminhoPai);
const items = await fs.promises.readdir(dirPath, { withFileTypes: true });
const arquivos = items.filter((item) => item.isFile()).map((item) => item.name);

if (arquivos.length > vetorArquivos.length) {
throw new Error(
`Não foi possível listar os arquivos pois o vetor passado é muito pequeno. O diretório escolhido possui ${arquivos.length} arquivos, mas o vetor passado comporta apenas ${vetorArquivos.length} elementos.`
);
}

arquivos.forEach((arquivo, i) => {
vetorArquivos[i] = arquivo;
});

for (let i = arquivos.length; i < vetorArquivos.length; i++) {
vetorArquivos[i] = '';
}
}

export async function listar_arquivos_por_tipo(
interpretador: InterpretadorInterface,
caminhoPai,
vetorArquivos,
vetorTipos
) {
const dirPath = path.resolve(caminhoPai);
const items = await fs.promises.readdir(dirPath, { withFileTypes: true });
const arquivos = items
.filter((item) => item.isFile() && vetorTipos.some((tipo) => item.name.endsWith(tipo)))
.map((item) => item.name);

if (arquivos.length > vetorArquivos.length) {
throw new Error(
`Não foi possível listar os arquivos pois o vetor passado é muito pequeno. O diretório escolhido possui ${arquivos.length} arquivos, mas o vetor passado comporta apenas ${vetorArquivos.length} elementos.`
);
}

arquivos.forEach((arquivo, i) => {
vetorArquivos[i] = arquivo;
});

for (let i = arquivos.length; i < vetorArquivos.length; i++) {
vetorArquivos[i] = '';
}
}

function obterProximoIndiceLivre() {
const indice = arquivos.findIndex((arquivo) => arquivo === null);
if (indice === -1) {
throw new Error('O número máximo de arquivos que podem ser abertos ao mesmo tempo foi atingido');
}
return indice;
}

function obterArquivo(endereco) {
const arquivo = arquivos[endereco];
if (!arquivo) {
throw new Error('O endereço de memória especificado não aponta para um arquivo');
}
return arquivo;
}

function arquivoAberto(caminho) {
return arquivos.some((arquivo) => arquivo && arquivo.caminho === caminho);
}
200 changes: 200 additions & 0 deletions testes/bibliotecas/arquivos.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { InterpretadorInterface } from '@designliquido/delegua/interfaces';
import fs from 'fs';
import {
abrir_arquivo,
fechar_arquivo,
fim_arquivo,
ler_linha,
escrever_linha,
substituir_texto,
arquivo_existe,
apagar_arquivo,
criar_pasta,
listar_pastas,
listar_arquivos,
listar_arquivos_por_tipo,
} from '../../fontes/bibliotecas/arquivos';

describe('Biblioteca Arquivos', () => {
beforeEach(async () => {
await fs.promises.mkdir('testDir', { recursive: true });
await fs.promises.writeFile('testDir/test.txt', 'Linha 1\nLinha 2');
});

afterEach(async () => {
try {
await fechar_arquivo({} as InterpretadorInterface, 0);
} catch (error) {}
try {
await fs.promises.rm('testDir');
} catch (error) {}
});

afterAll(async () => {
try {
await fs.promises.unlink('testDir/test.txt');
await fs.promises.rm('testDir', { recursive: true });
} catch (error) {}
});

describe('Abrir Arquivo', () => {
it('Trivial', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 0);
expect(arquivoIndex).toBeGreaterThanOrEqual(0);
});

it('Modo de acesso invalido', async () => {
await expect(abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 3)).rejects.toThrow(
'Modo de acesso inválido'
);
});

it('Arquivo já está aberto', async () => {
await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 0);
await expect(abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 0)).rejects.toThrow(
"O arquivo 'testDir/test.txt' já está aberto"
);
});
});

describe('Fechar Arquivo', () => {
it('Trivial', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 0);
await fechar_arquivo({} as InterpretadorInterface, arquivoIndex);
await expect(fs.promises.access('testDir/test.txt')).resolves.toBeUndefined();
});

it('Endereço invalido', async () => {
await expect(fechar_arquivo({} as InterpretadorInterface, 99)).rejects.toThrow(
'O endereço de memória especificado não aponta para um arquivo'
);
});
});

describe('Fim Arquivo', () => {
it('Trivial', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 2);
expect(fim_arquivo({} as InterpretadorInterface, arquivoIndex)).toBe(false);
});

it('Fim não alcançado', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 0);
await ler_linha({} as InterpretadorInterface, arquivoIndex);
expect(fim_arquivo({} as InterpretadorInterface, arquivoIndex)).toBe(false);
});
});

describe('Ler Linha', () => {
it('Trivial', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 0);
const linha = await ler_linha({} as InterpretadorInterface, arquivoIndex);
expect(linha).toBe('Linha 1');
});

it('Erro caso em modo de escrita', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 1);
await expect(ler_linha({} as InterpretadorInterface, arquivoIndex)).rejects.toThrow(
"O arquivo 'testDir/test.txt' está aberto em modo de escrita"
);
});
});

describe('Escrever Linha', () => {
it('Trivial', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 1);
await escrever_linha({} as InterpretadorInterface, 'Nova linha', arquivoIndex);
const data = await fs.promises.readFile('testDir/test.txt', 'utf-8');
expect(data).toContain('Nova linha');
});

it('Arquivo em modo leitura', async () => {
const arquivoIndex = await abrir_arquivo({} as InterpretadorInterface, 'testDir/test.txt', 0);
await expect(escrever_linha({} as InterpretadorInterface, 'Nova linha', arquivoIndex)).rejects.toThrow(
"O arquivo 'testDir/test.txt' está aberto em modo de leitura"
);
});
});
describe('Substituir Texto', () => {
it('Trivial', async () => {
await substituir_texto({} as InterpretadorInterface, 'testDir/test.txt', 'Linha 1', 'Linha Alterada', true);
const data = await fs.promises.readFile('testDir/test.txt', 'utf-8');
expect(data).toContain('Linha Alterada');
expect(data).not.toContain('Linha 1');
});

it('Varias instancias de um texto', async () => {
await substituir_texto({} as InterpretadorInterface, 'testDir/test.txt', 'Linha', 'Linha Alterada', false);
const data = await fs.promises.readFile('testDir/test.txt', 'utf-8');
expect(data.match(/Linha Alterada/g)).toHaveLength(2);
});
});

describe('Arquivo Existe', () => {
it('Verdadeiro caso exista', async () => {
expect(await arquivo_existe({} as InterpretadorInterface, 'testDir/test.txt')).toBe(true);
});

it('Falso caso não exista', async () => {
expect(await arquivo_existe({} as InterpretadorInterface, 'testDir/nonexistent.txt')).toBe(false);
});
});

describe('Apagar Arquivo', () => {
it('Trivial', async () => {
await apagar_arquivo({} as InterpretadorInterface, 'testDir/test.txt');
await expect(fs.promises.access('testDir/test.txt')).rejects.toThrow();
});
});

describe('Cria Pasta', () => {
it('Trivial', async () => {
await criar_pasta({} as InterpretadorInterface, 'testDir/subDir');
const exists = await fs.promises
.access('testDir/subDir')
.then(() => true)
.catch(() => false);
expect(exists).toBe(true);
});
});

describe('Listar Pastas', () => {
it('Trivial', async () => {
const vetorPastas = new Array(1);
await listar_pastas({} as InterpretadorInterface, 'testDir', vetorPastas);
expect(vetorPastas).toEqual(['subDir']);
});

it('Vetor muito pequeno', async () => {
const vetorPastas = new Array(0);
await expect(listar_pastas({} as InterpretadorInterface, 'testDir', vetorPastas)).rejects.toThrow();
});
});

describe('Listar Arquivos', () => {
it('Trivial', async () => {
const vetorArquivos = new Array(1);
await listar_arquivos({} as InterpretadorInterface, './testDir', vetorArquivos);
expect(vetorArquivos).toContain('test.txt');
});

it('Vetor muito pequeno', async () => {
const vetorArquivos = new Array(0);
await expect(listar_arquivos({} as InterpretadorInterface, './testDir', vetorArquivos)).rejects.toThrow();
});
});

describe('Listar Arquivos Por Tipo', () => {
it('Trivial', async () => {
const vetorArquivos = new Array(1);
await listar_arquivos_por_tipo({} as InterpretadorInterface, './testDir', vetorArquivos, ['.txt']);
expect(vetorArquivos).toContain('test.txt');
});

it('Vetor muito pequeno', async () => {
const vetorArquivos = new Array(0);
await expect(
listar_arquivos_por_tipo({} as InterpretadorInterface, './testDir', vetorArquivos, ['.txt'])
).rejects.toThrow();
});
});
});

0 comments on commit d3a3aa1

Please sign in to comment.