\documentclass{article}

\usepackage{graphicx}
\usepackage{xcolor}
\usepackage{minted}
\usepackage{hyperref}

\title{CS3502 Project 3: File Manager}
\author{Kiana Sheibani}

\begin{document}

\maketitle
\newpage

\section{Introduction}

Circumstance compels us to program a GUI file manager from scratch, as it so
often does.

\subsection{Objective}

A file manager is a tool for reading and manipulating a file-system, typically
the primary file-system of the machine. It allows the user to perform various
operations on the files and directories (\emph{entries} in aggregate) of the
file-system, such as to:

\begin{itemize}
  \item \textbf{Create} new entries;
  \item \textbf{Read} and \textbf{Update} the contents of entries;
  \item \textbf{Delete} entries.
\end{itemize}

Commonly, file managers are designed to allow this manipulation by having the
user traverse the filesystem one directory at a time, moving from a single
working directory to its sub- or parent directories.

\subsection{Resources}

Implementing a file manager is not a trivial task. Properly navigating a
file-system requires quite a bit of engineering around complex systems,
unintuitive edge cases and strange errors, and some languages and frameworks are
better equipped to handle this than others.

After considering this fact, I decided on \href{https://rust-lang.org/}{Rust} as
my language of choice. Not only do I have significant prior experience with it,
but its low-level nature and near-slavish dedication to correctness make it
ideal for working with file-systems.

Once I had my language picked, I searched for GUI libraries I could use for the
front-end. It was not long before I found
\href{https://crates.io/crates/xilem/}{Xilem}, an experimental cross-platform UI
toolkit. It was still somewhat unstable, but I decided it would be perfectly
fine for my purposes.%
\footnote{This assumption turned out to be rather short-sighted, but I made it
  work as best I could.}

\section{Design and Architecture}

\subsection{Front-End Design}

\begin{figure}[tbh]
  \includegraphics[width=\linewidth]{assets/gui.png}
  \caption{The UI of the file manager.}
  \label{fig:gui}
\end{figure}

The UI design of the file manager is somewhat inspired by
\href{https://github.com/ranger/ranger}{{\texttt{ranger}}}, though heavily
simplified. The current directory path is displayed at the top, and its entries
are listed in the main panel below it. When a file is opened, its contents
appear on a panel to the right, and remain there until another file is opened.

The file-system can be navigated by either double-clicking on a subdirectory, or
selecting it and then clicking the \emph{Open} button at the bottom. To move up
the directory tree, a component of the topmost path display can be clicked on.
There are also several other action buttons along the bottom; these operate on
the currently selected entry (aside from the \emph{New File} and \emph{New Dir}
actions) and using them pops up a dialog box to confirm the action. The dialog
box popup is also used to display errors whenever they occur.

\begin{figure}[tbh]
  \includegraphics[width=\linewidth]{assets/dialog.png}
  \caption{The file manager showing a file renaming dialog.}
  \label{fig:dialog}
\end{figure}

\subsection{Program Architecture}

The structure of the program can be conceptually divided into two halves: the
file-system management subroutines, and the GUI logic.

\subsubsection{File-System Management}

Internally, an entry in the current directory is stored as an \texttt{EntryData}
object (Listing~\ref{listing:EntryData}), which holds the entry's name and other
metadata. The basic file-system operations, such as renaming and deleting
entries, all operate on this data structure.

\begin{listing}[!ht]
\inputminted[
firstline=7, lastline=17,
fontsize=\footnotesize,
linenos,
frame=lines,
framesep=2mm,
baselinestretch=1.1,
]{rust}{../src/files.rs}
\vspace*{-\baselineskip}
\caption{\texttt{files.rs} --- Definition of \texttt{EntryData}}
\label{listing:EntryData}
\end{listing}

Reading and editing a file, on the other hand, requires a different structure to
store and manipulate the file handle. This is defined as \texttt{FileData}
(Listing~\ref{listing:FileData}). When a file is opened, the file handle is
retained for the entire duration the file is viewed, only being closed when the
file is exited. The \texttt{files} module of the program implements an
\texttt{open} function to create this data structure and a \texttt{save}
function to write to the file handle that it holds.

\begin{listing}[!ht]
\inputminted[
firstline=72, lastline=85,
fontsize=\footnotesize,
linenos,
frame=lines,
framesep=2mm,
baselinestretch=1.1,
]{rust}{../src/files.rs}
\vspace*{-\baselineskip}
\caption{\texttt{files.rs} --- Definition of \texttt{FileData}}
\label{listing:FileData}
\end{listing}

\subsubsection{GUI \& Error Handling}

Xilem is a reactive GUI framework based on composable widgets, similar to Qt.
The system is based around a data structure representing the app's state; in the
case of this program, the \texttt{AppState} type includes the current directory,
selected entry, and state for any open panels, such as the \texttt{FileData} for
the open file.

\texttt{AppState} also includes the \texttt{DialogState}, which is an enum with
one value per type of dialog that can be opened: \texttt{NewFile},
\texttt{NewDir}, \texttt{Rename}, \texttt{Delete}, and \texttt{Error}, where the
last value also includes an error message to display. Determining when to show
these errors is simple: thanks to Rust's aforementioned obsession with
correctness, every file-system routine it provides that could ever conceivably
result in any sort of error wraps its return type in a \texttt{std::io::Result}
data structure, which forces the programmer to handle the error case before the
actual output can be retrieved. This is why I chose Rust for this project, and
it led to very simple and easy-to-understand file management code.

\section{Testing \& Edge Cases}

In order to ensure the robustness of my work, I regularly tested it manually to
make sure that it functioned properly and handled any of the weird edge cases
file-system code is prone to. Some of these issues I was able to fully
eliminate, while others were a bit more stubborn.

\subsection{File-System Race Conditions}

The file manager is only one of many processes that can access the file-system,
which can lead to issues if two processes are trying to modify the same
directory at the same time. For example, if another process deletes a file in
the current directory of the file manager, an orphan entry may be left over that
no longer points to an existing file. While the file manager will not
automatically detect that this has occurred, it will throw an error if the user
tries to access an entry that no longer exists.

\subsection{Uncommon Entry Types}

So far, the only two entry types mentioned have been files and subdirectories,
the two most common types. There are others, however; symbolic links, or
``symlinks'', and more esoteric file types like named pipes, device files, etc.
Symlinks are of particular concern, as they violate the file-system's otherwise
perfect tree structure.

To avoid hard-to-debug issues down the line, I decided on a policy for handling
symlinks from the start:

\begin{itemize}
  \item In the main panel, symlinks are not followed and are displayed
        distinctly from the entries that they point to, including their
        metadata. This is done for performance reasons.
  \item Opening a symlink follows it to its target, with the caveat that
        if a symlink is followed into a subdirectory, the displayed path is
        canonicalized. This simplifies the navigation.
\end{itemize}

All other entry types simply throw an error when the user tries to open them, as
do any files that do not contain UTF-8 text.

\subsection{Unusual Entry Names}

While most entry names are simple Latin letters and numbers, depending on the
operating system there are many other possibilities. Unix-based systems
typically have UTF-8 entry names, though they technically allow any string of
bytes except for \texttt{0x00} (NUL) or \texttt{0x2F} (/). Windows, on the other
hand, has historically used UTF-16 for its entry names, as it predated the wide
adoption of the superior UTF-8.

To allow cross-platform programs to navigate this confusion, the Rust standard
library provides the \texttt{OsString} type, which represents a string in the
format the target OS prefers. Due to this and some careful programming, the file
manager will never crash due to unsupported entry names (at least on Linux; I
have not personally tested Windows). Unfortunately, while non-Unicode entry
names can be read, they cannot be entered into dialog text fields. This is an
inherent limitation of Xilem that is unlikely to change.

\subsection{Large File Sizes}

This is the largest issue that still remains in the file manager. Due to the
fact that a full buffer of an open file's contents must be maintained, the file
editor is exceedingly slow with files over a few kilobytes, and trying to open
anything larger than a megabyte has a chance of causing the application to
crash. Ideally, the program would use some sort of line buffering scheme to
avoid having to update the file all at once, but I was not able to implement
anything that worked well.

\section{Conclusion}

After roughly a week's effort, a basic file manager has been achieved.  Though
the visual aesthetic and the file editing feature may be improved, this
serves as a strong foundation for a well-programmed application.

\subsection{Learning Outcomes}

While I most likely started from a more knowledgeable place on this subject than
most in this class, I did learn some new things from this process. I was not
nearly as knowledgeable on the minutiae of OS file-systems when I started this
project as I am now, nor was I as experienced in writing GUI applications that
properly interface with that OS minutia. At the end of it all, I am glad that
something of some use came out of this.

\end{document}
