Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Criação da biblioteca Arquivos #32

Merged
merged 1 commit into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
});
});
});
Loading