- Implemented `report.nim` to create structured reports on metadata modifications. - Added functionality to merge reports and convert them to formatted strings. docs: Create prompt documentation for Markdown parser project - Added `prompt.md` detailing the requirements and functionalities for the Markdown parser. - Included specifications, usage examples, and testing guidelines. docs: Generate code review report for Markdown parser - Created `rapport_revue_code.md` outlining security vulnerabilities, code quality issues, and suggested improvements. - Provided a detailed analysis of the codebase with actionable recommendations. test: Add test data for Markdown parser - Included various Markdown files and a JPG image in `test_data` to simulate different scenarios. - Ensured that the parser can handle both valid and invalid metadata. chore: Add version management file - Created `version.nim` for automatic versioning of the Markdown parser. - Established constants for major, minor, patch, and build versions.
210 lines
7.5 KiB
Nim
210 lines
7.5 KiB
Nim
# fileutils.nim
|
|
# Module responsable des opérations sur les fichiers et répertoires
|
|
|
|
import std/[os, strutils, strformat, options, tables]
|
|
import zippy/ziparchives
|
|
import metadata, report, config
|
|
|
|
|
|
proc printOutput*(output: string, debug: bool = false, appConfig: Option[AppConfig] = none(AppConfig)) =
|
|
## Affiche un message en sortie standard
|
|
## Si debug est true, n'affiche le message que si debugModeActive est activé dans la configuration
|
|
## Si debug est false, affiche toujours le message
|
|
##
|
|
## Paramètres:
|
|
## - output: Le message à afficher
|
|
## - debug: Si true, il s'agit d'un message de débogage
|
|
## - appConfig: Configuration de l'application (optionnel)
|
|
if not debug:
|
|
# Message normal, toujours affiché
|
|
echo output
|
|
else:
|
|
# Message de débogage, affiché uniquement si debugModeActive est true
|
|
if appConfig.isSome and appConfig.get().debugModeActive:
|
|
echo fmt"[DEBUG] {output}"
|
|
|
|
proc debugLog*(message: string, appConfig: AppConfig) =
|
|
## Affiche un message de débogage si le mode debug est activé
|
|
if appConfig.debugModeActive:
|
|
echo fmt"[DEBUG] {message}"
|
|
|
|
proc walkDirRecFiles*(dir: string, ext: string): seq[string] =
|
|
## Parcourt récursivement un répertoire et retourne tous les fichiers avec l'extension spécifiée
|
|
result = @[]
|
|
|
|
for kind, path in walkDir(dir):
|
|
if kind == pcDir:
|
|
result.add(walkDirRecFiles(path, ext))
|
|
elif kind == pcFile and path.endsWith(ext):
|
|
result.add(path)
|
|
|
|
proc readMarkdownFile*(filePath: string): string =
|
|
## Lit le contenu d'un fichier Markdown
|
|
try:
|
|
return readFile(filePath)
|
|
except:
|
|
echo fmt"Erreur lors de la lecture du fichier: {filePath}"
|
|
return ""
|
|
|
|
proc writeMarkdownFile*(filePath: string, content: string): bool =
|
|
## Écrit le contenu dans un fichier Markdown
|
|
try:
|
|
echo fmt"DEBUG: Tentative d'écriture dans le fichier: {filePath}"
|
|
echo fmt"DEBUG: Contenu à écrire (premiers 100 caractères): {content[0..min(99, content.len-1)]}"
|
|
|
|
let parentDir = parentDir(filePath)
|
|
if not dirExists(parentDir):
|
|
echo fmt"DEBUG: Création du répertoire parent: {parentDir}"
|
|
createDir(parentDir)
|
|
|
|
# Normalisation des retours à la ligne pour éviter les problèmes de \n littéraux
|
|
let normalizedContent = content.replace("\n", "\r\n")
|
|
|
|
echo fmt"DEBUG: Écriture de {normalizedContent.len} caractères"
|
|
writeFile(filePath, normalizedContent)
|
|
echo fmt"DEBUG: Fichier écrit avec succès"
|
|
return true
|
|
except Exception as e:
|
|
echo fmt"Erreur lors de l'écriture dans le fichier: {filePath}"
|
|
echo fmt"DEBUG: Exception: {e.msg}"
|
|
return false
|
|
|
|
proc copyFileWithDirectories*(sourcePath, targetPath: string, verbose: bool = false): bool =
|
|
## Copie un fichier de sourcePath vers targetPath en créant les répertoires
|
|
## intermédiaires si nécessaire
|
|
##
|
|
## Paramètres:
|
|
## sourcePath: Chemin source du fichier
|
|
## targetPath: Chemin destination du fichier
|
|
## verbose: Affiche des informations supplémentaires
|
|
##
|
|
## Retourne:
|
|
## true si la copie a réussi, false sinon
|
|
try:
|
|
if verbose:
|
|
echo fmt"Copie du fichier: {sourcePath} -> {targetPath}"
|
|
|
|
let parentDir = parentDir(targetPath)
|
|
if not dirExists(parentDir):
|
|
createDir(parentDir)
|
|
|
|
copyFile(sourcePath, targetPath)
|
|
return true
|
|
except Exception as e:
|
|
echo fmt"Erreur lors de la copie du fichier: {e.msg}"
|
|
return false
|
|
|
|
proc zipDir*(sourceDir: string, zipFilePath: string = "", verbose: bool = false): bool =
|
|
## Crée une archive zip du répertoire source dans un répertoire "backup"
|
|
## Si zipFilePath est vide, utilise le nom du répertoire source pour le zip
|
|
try:
|
|
let
|
|
sourceDirName = sourceDir.splitPath().tail
|
|
parentDir = sourceDir.splitPath().head
|
|
backupDir = parentDir / "backup"
|
|
|
|
# Créer le répertoire backup s'il n'existe pas
|
|
if not dirExists(backupDir):
|
|
if verbose:
|
|
echo fmt"Création du répertoire backup: {backupDir}"
|
|
createDir(backupDir)
|
|
|
|
# Déterminer le nom du fichier zip
|
|
var actualZipPath: string
|
|
if zipFilePath == "":
|
|
# Si aucun nom n'est fourni, utiliser le nom du répertoire source
|
|
actualZipPath = backupDir / fmt"{sourceDirName}.zip"
|
|
else:
|
|
# Si un nom est fourni, l'utiliser mais le placer dans le répertoire backup
|
|
actualZipPath = backupDir / zipFilePath.extractFilename()
|
|
|
|
if verbose:
|
|
echo fmt"Création d'une archive zip: {actualZipPath} à partir de {sourceDir}"
|
|
|
|
# Utilisons une approche plus simple basée sur la documentation de zippy
|
|
# Extrayons d'abord tous les fichiers avec leurs chemins relatifs
|
|
var fileEntries = initTable[string, string]()
|
|
|
|
# Fonction récursive pour ajouter les fichiers
|
|
proc addFilesRecursively(dir: string, relativePath = "") =
|
|
for kind, path in walkDir(dir):
|
|
let relPath = if relativePath.len > 0: relativePath / path.extractFilename else: path.extractFilename
|
|
|
|
if kind == pcFile:
|
|
# Ajouter le fichier à l'archive
|
|
if verbose:
|
|
echo fmt"Ajout du fichier: {path}"
|
|
fileEntries[relPath] = readFile(path)
|
|
elif kind == pcDir:
|
|
# Récursivement parcourir les sous-répertoires
|
|
addFilesRecursively(path, relPath)
|
|
|
|
# Commencer la compression depuis le répertoire source
|
|
addFilesRecursively(sourceDir)
|
|
|
|
# Créer l'archive avec toutes les entrées
|
|
let zipData = ziparchives.createZipArchive(fileEntries)
|
|
|
|
# Sauvegarder l'archive zip
|
|
writeFile(actualZipPath, zipData)
|
|
|
|
if verbose:
|
|
echo fmt"Archive zip créée avec succès: {actualZipPath}"
|
|
|
|
return true
|
|
except Exception as e:
|
|
echo fmt"Exception lors de la création du zip: {e.msg}"
|
|
return false
|
|
|
|
proc processMarkdownFileBasic*(sourcePath, targetPath: string, verbose: bool = false): Report =
|
|
## Traite un fichier Markdown : lit, analyse les métadonnées, complète si nécessaire, et écrit
|
|
var report = newReport()
|
|
|
|
if verbose:
|
|
echo fmt"Traitement du fichier: {sourcePath}"
|
|
|
|
let content = readMarkdownFile(sourcePath)
|
|
if content == "":
|
|
report.errors.add(fmt"Impossible de lire le fichier: {sourcePath}")
|
|
return report
|
|
|
|
# Extraire les métadonnées existantes ou créer de nouvelles métadonnées
|
|
var metadata: Metadata
|
|
let metadataOpt = extractMetadataFromYaml(content)
|
|
|
|
# Utiliser correctement l'API Option
|
|
if metadataOpt.isSome():
|
|
metadata = metadataOpt.get()
|
|
else:
|
|
metadata = newMetadata()
|
|
|
|
# Compléter les métadonnées manquantes
|
|
echo ">>> Avant updateMetadata: catégorie = " & metadata.category
|
|
# Utiliser le targetPath pour déterminer la catégorie au lieu du sourcePath
|
|
let modified = updateMetadata(metadata, content, targetPath, true, true)
|
|
echo ">>> Après updateMetadata: catégorie = " & metadata.category
|
|
|
|
if verbose:
|
|
if metadata.isNew:
|
|
echo fmt"Métadonnées ajoutées pour: {sourcePath}"
|
|
elif modified:
|
|
echo fmt"Métadonnées mises à jour pour: {sourcePath}"
|
|
else:
|
|
echo fmt"Métadonnées déjà complètes pour: {sourcePath}"
|
|
|
|
# Ajouter les métadonnées au contenu
|
|
let newContent = addMetadataToContent(metadata, content)
|
|
|
|
# Écrire le nouveau contenu dans le fichier cible
|
|
if writeMarkdownFile(targetPath, newContent):
|
|
if metadata.isNew:
|
|
report.newMetadata.add(targetPath)
|
|
elif modified:
|
|
report.updatedMetadata.add(targetPath)
|
|
else:
|
|
report.unchanged.add(targetPath)
|
|
else:
|
|
report.errors.add(fmt"Impossible d'écrire dans le fichier: {targetPath}")
|
|
|
|
return report
|