denominate-0.5.0: Functions supporting bulk file and directory name normalization.

MaintainerCalvin Smith <>



Functions to assist in renaming of directories and files into a standard, normalized format.

This module defines several functions supporting renaming of files and directories, and is especially useful for doing a bulk renaming of all files and directories, recursively, in a given base directory.

The primary functions of interest are rename and renameAll, both of which accept a function for creating the new filename based on its current name. The user may supply a custom filename converter function, or may use the pre-defined function that this module defines.

The standard pre-defined converter determines the new name for a file or directory using the following rules:

  1. all letters are converted to lowercase;
  2. all non-alphanumeric characters at the beginning of a file or directory name are removed (with the exception of an initial .);
  3. all non-alphanumeric characters at the end of a directory name or the end of a filename (before the extension) are removed;
  4. all other blocks of one or more non-alphanumeric characters are converted to a single hyphen.

See the documentation of the exported functions for more information.



data FileType Source

Represents the type of a file or directory. These are exhaustive, as for purposes of this module, we consider everything that isn't a directory to be a file.



data RenameResult Source

Represents the result of a rename attempt, which either fails or succeeds. In both cases, the typed file path is that of the file for which a rename attempt was made. Upon failure, the string parameter contains information about the error (may have been an os-level error or user error); upon success, the string parameter is the name to which the file was renamed (which includes the case that no change was performed because old and new names were equal).

type TypedFilePath = (FileType, FilePath)Source

Represents a filepath together with the type of file to which the path refers.

type FilenameConverter = TypedFilePath -> FilePathSource

A filename converter maps an old filename to a new filename. A converter takes a typed file representing a directory name or a filename without extension, and only needs to determine the new name based on the old name. It does not need to worry about extracting the file path from an absolute path or determining the file extension, as all functions in this module that use a FilenameConverter will only pass in typed files containing the last directory (if a directory) or the filename without extension if a file.

normalizeFilename :: FilenameConverter -> TypedFilePath -> StringSource

Normalize the filename of the given typed file path using the supplied FilenameConverter function, which will be passed the directory name (without parent directories) in case of a directory or the filename (without any parent directories or the extension) in case of a file. This function takes care of extracting the part of the path that is to be normalized, calling the user-supplied function with only that part, and then reassembling the result of the filename converter into a full path again.

allFilepaths :: FilePath -> IO [TypedFilePath]Source

Generate a list of all files and directories below the given directory, in depth-first order such that all files in a given directory appear before the directory or any of its parent directories.

rename :: FilenameConverter -> TypedFilePath -> IO RenameResultSource

Rename a single file or directory using the supplied filename converter, which will be passed just the directory name (without any parent directories or a terminal slash) in the case of a directory or just the filename without the extension in the case of a file. If there already exists a file with the intended new name, nothing is done. If the new name is the same as the old name (not considering file extension), then the function successfully returns without touching the filesystem. In all cases where a file is renamed, the extension of a file will be automatically converted to lowercase but otherwise remains the same (no characters are ever removed from the extension).

renameAll :: FilenameConverter -> TypedFilePath -> IO [RenameResult]Source

Rename all files and directories, recursively, in the given directory, using the supplied filename converter to determine the new name of each file or directory. The converter function will be called once for each file or directory, and will be passed just the directory name (without the parent directories) in the case of a directory or just the filename without the extension in the case of a file. The extension of files (but not directories if they seem to have an extension) will be converted to lower case, but is not otherwise changed. There will be one RenameResult for each success or failure, and an indication of the reason for failure for failures, or the new name in case of success.

fileToTypedFilePath :: FilePath -> IO TypedFilePathSource

Convert a filepath to a TypedFilePath. A Directory is a file for which Directory.doesDirectoryExist returns true. If the path does not represent a directory, it is considered a file, and there is no further testing to verify that a file with that path actually exists.

defaultFilenameConverter :: FilenameConverterSource

The default filename converter, which normalizes a filename by converting letters to lowercase and converting one or more undesirable characters into a single hyphen (or removing altogether if at the beginning or the end of the name). The only exception to these rules is that an initial dot of a filename is not removed.