I implemented an in-memory file system that should support basic Linux commands (mkdir, cd, mkfile...) on the current working directory. It can read commands from a file and process them.
I'm interested in a review from senior developers to see what makes a difference between a junior and senior (what should I focus on...)
#include <iostream>
#include <chrono>
#include <ctime>
#include <vector>
#include <algorithm>
#include <memory>
#include <fstream>
#include <sstream>
class Directory;
enum class EntryType
{
FILE,
DIRECTORY,
ANY_TYPE
};
class Entry {
private:
const std::string m_name;
const std::time_t m_created;
const EntryType m_entryType;
Directory *m_parentDirectory;
protected:
Entry(const std::string &name, EntryType entryType, Directory *parentDirectory) : m_name {name},
m_created {std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())},
m_entryType {entryType},
m_parentDirectory {parentDirectory}
{
}
virtual ~Entry()
{
}
public:
const std::string& getName() const {return m_name;}
const std::time_t& getTimeCreated() const {return m_created;}
const EntryType& getEntryType() const {return m_entryType;}
Directory* getParentDirectory() {return m_parentDirectory;}
};
class File : public Entry {
public:
File(const std::string &name, Directory *parentDirectory) : Entry(name, EntryType::FILE, parentDirectory)
{
}
};
class Directory : public Entry {
private:
std::vector<std::shared_ptr<Entry>> entries;
public:
Directory(const std::string &name, Directory *parentDirectory) : Entry(name, EntryType::DIRECTORY, parentDirectory)
{
}
~Directory()
{
}
private:
void list(EntryType type)
{
for (auto &entry : entries) {
if (entry->getEntryType() == type)
std::cout << getCreatedTime(entry.get()) << "\t\t" << entry->getName() << "\n";
}
}
int countEntries(EntryType type)
{
auto count {static_cast<int>(std::count_if(entries.begin(), entries.end(), [&type](std::shared_ptr<Entry> entry){
return entry->getEntryType() == type;
}))};
return count;
}
bool checkEntry(const std::string &name)
{
auto findEntry {std::find_if(entries.begin(), entries.end(), [&name](std::shared_ptr<Entry> entry){
return entry->getName() == name;
})};
if (findEntry != entries.end())
return true;
return false;
}
const std::string getCreatedTime (Entry *entry) const
{
std::string timeCreated {std::ctime(&entry->getTimeCreated())};
timeCreated[timeCreated.length() - 1] = '\0';
return timeCreated;
}
public:
void listEntries()
{
std::cout << getCreatedTime(this) << "\t\t" << ".\n";
int numDir {1};
if (this->getParentDirectory() != nullptr) {
std::cout << getCreatedTime(this->getParentDirectory()) << "\t\t" << "..\n";
++numDir;
}
list(EntryType::DIRECTORY);
list(EntryType::FILE);
std::cout << "\n";
std::cout << countEntries(EntryType::DIRECTORY) + numDir << "\t\t" << "Dir(s)\n";
std::cout << countEntries(EntryType::FILE) << "\t\t" << "File(s)\n";
std::cout << "\n";
std::cout << "--------------------------------------------------\n";
}
void makeDirectory(const std::string &name)
{
if (checkEntry(name))
return;
entries.push_back(std::make_shared<Directory>(name, this));
}
void makeFile(const std::string &name)
{
if (checkEntry(name))
return;
entries.push_back(std::make_shared<File>(name, this));
}
Directory* changeDirectory(const std::string &name)
{
if (name == ".") {
return this;
} else if (name == "..") {
if (this->getParentDirectory() == nullptr) {
return this;
} else
return this->getParentDirectory();
}
for (auto &entry : entries) {
if (entry->getEntryType() == EntryType::DIRECTORY && entry->getName() == name)
return static_cast<Directory *>(entry.get());
}
std::cout << "Directory: " << name << " does not exist\n";
return this;
}
void deleteEntry(const std::string &name, EntryType type)
{
auto findEntry {std::find_if(entries.begin(), entries.end(), [&name](std::shared_ptr<Entry> entry){
return entry->getName() == name;
})};
if (findEntry != entries.end()) {
if (type == EntryType::ANY_TYPE || type == findEntry->get()->getEntryType()) {
entries.erase(findEntry);
return;
}
}
}
};
class FileSystem {
private:
Directory m_root;
Directory *m_currentDirectory;
public:
FileSystem() : m_root(Directory {".", nullptr})
{
m_currentDirectory = &m_root;
}
~FileSystem()
{
}
private:
void ls() const
{
m_currentDirectory->listEntries();
}
void mkdir(const std::string &name) const
{
m_currentDirectory->makeDirectory(name);
}
void mkfile(const std::string &name) const
{
m_currentDirectory->makeFile(name);
}
void dir() const
{
ls();
}
void cd(const std::string &name)
{
m_currentDirectory = m_currentDirectory->changeDirectory(name);
}
void del(const std::string &name, EntryType type = EntryType::ANY_TYPE) const
{
m_currentDirectory->deleteEntry(name, type);
}
void rmdir(const std::string &name) const
{
del(name, EntryType::DIRECTORY);
}
void processCommand(const std::string &command, const std::string &option)
{
if (command == "ls")
return ls();
else if (command == "dir")
return dir();
else if (command == "mkdir")
return mkdir(option);
else if (command == "mkfile")
return mkfile(option);
else if (command == "cd")
return cd(option);
else if (command == "rmdir")
return rmdir(option);
else if (command == "del")
return del(option);
}
public:
void readInput(const std::string &fileName)
{
std::ifstream infile {fileName};
std::string line;
while (std::getline(infile, line)) {
std::istringstream iss(line);
std::string command;
std::string option;
iss >> command;
iss >> option;
std::cout << command << " " << option << "\n";
processCommand(command, option);
}
}
};
int main() {
FileSystem filesystem;
filesystem.readInput("path to the input file");
return 0;
}