-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Criação da biblioteca Arquivos (#32)
- Loading branch information
Showing
2 changed files
with
397 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); | ||
}); |