inline std::ostream &operator<<(std::ostream &out, FileSystem const &rhs)
{
    return out << rhs.d_path.string();
}

inline bool operator==(FileSystem const &lhs, FileSystem const &rhs)
{
    return lhs.sameAs(rhs);
}

template <typename Type>
FileSystem &FileSystem::operator/=(Type const &arg)
{
    d_path /= arg;
    return *this;
}

inline FileSystem &FileSystem::operator/=(FileSystem const &arg)
{
    return *this /= arg.d_path;
}

template <typename Type>
FileSystem &FileSystem::operator+=(Type const &arg)
{
    d_path += arg;
    return *this;
}

inline FileSystem &FileSystem::operator+=(FileSystem const &arg)
{
    return *this += arg.d_path;
}

inline char const *FileSystem::c_str() const
{
    return d_path.c_str();
}

inline std::string FileSystem::extension() const
{
    return d_path.extension();
}

inline std::string FileSystem::filename() const
{
    return d_path.filename();
}

inline FileSystem::Path const &FileSystem::path() const
{
    return d_path;
}

inline FileSystem FileSystem::stem() const
{
    return d_path.stem();
}

inline std::string FileSystem::string() const
{
    return d_path.string();
}

inline bool FileSystem::isAbsolute() const
{
    return d_path.is_absolute();
}

inline bool FileSystem::hasExtension() const
{
    return d_path.has_extension();
}

inline bool FileSystem::hasFilename() const
{
    return d_path.has_filename();
}

inline bool FileSystem::isRelative() const
{
    return d_path.is_relative();
}

inline FileSystem FileSystem::parent() const
{
    return d_path.parent_path();
}

inline FileSystem FileSystem::relative() const
{
    return d_path.relative_path();
}

inline void FileSystem::clear()
{
    d_path.clear();
}

inline Ranger<FileSystem::Iter> FileSystem::range()
{
    return Ranger<Iter>{ d_path.begin(), d_path.end() };
}

inline Ranger<FileSystem::ConstIter> FileSystem::range() const
{
    return Ranger<ConstIter>{ d_path.begin(), d_path.end() };
}

inline FileSystem &FileSystem::operator()(EC &ec)
{
    return set(&ec);
}

inline FileSystem const &FileSystem::operator()(EC &ec) const
{
    return set(&ec);
}

inline FileSystem &FileSystem::noEC()
{
    return set(0);
}

inline FileSystem const &FileSystem::noEC() const
{
    return set(0);
}

inline FileSystem FileSystem::absolute() const
{
    namespace fs = std::filesystem;
    return optEC1<Path>(fs::absolute, fs::absolute);
//    return absCan(fs::absolute, fs::absolute);
}

inline FileSystem FileSystem::canonical() const
{
    namespace fs = std::filesystem;
    return optEC1<Path>(fs::canonical, fs::canonical);
//    return absCan(fs::canonical, fs::canonical);
}

inline bool FileSystem::copy(FileSystem const &dest, FSOptions cpOptions)
{
    return copy(dest.d_path, cpOptions);
}

inline bool FileSystem::mkDir() const
{
    namespace fs = std::filesystem;
    return optEC1(fs::create_directory, fs::create_directory);
}

inline bool FileSystem::mkDir(Path const &reference) const
{
    namespace fs = std::filesystem;
    return optEC2(reference, fs::create_directory, fs::create_directory);
}

inline bool FileSystem::mkDir(FileSystem const &reference) const
{
    return mkDir(reference.d_path);
}

inline bool FileSystem::mkDirs() const
{
    namespace fs = std::filesystem;
    return optEC1(fs::create_directories, fs::create_directories);
}

// static 
inline FileSystem FileSystem::cwd()
{
    return std::filesystem::current_path();
}

// static 
inline FileSystem FileSystem::cwd(EC &ec)
{
    return std::filesystem::current_path(ec);
}

inline bool FileSystem::sameAs(Path const &rhs) const
{
    namespace fs = std::filesystem;
    return d_ec == 0 ?
                fs::equivalent(d_path, rhs)
            :
                fs::equivalent(d_path, rhs, *d_ec);
}

inline bool FileSystem::sameAs(FileSystem const &rhs) const
{
    return sameAs(rhs.d_path);
}

inline bool FileSystem::exists() const
{
     namespace fs = std::filesystem;
    return optEC1(fs::exists, fs::exists);
}

// static
inline bool FileSystem::exists(FileStatus status)
{
    return std::filesystem::exists(status);
}

template <typename ReturnType>
inline ReturnType FileSystem::optEC1(
                            ReturnType (*fun1)(Path const &),
                            ReturnType (*fun2)(Path const &, EC &)) const
{
    return d_ec == 0 ? fun1(d_path) : fun2(d_path, *d_ec);
}

inline std::uintmax_t FileSystem::size() const
{
    namespace fs = std::filesystem;
    return optEC1(fs::file_size, fs::file_size);
}

inline std::uintmax_t FileSystem::count() const
{
    namespace fs = std::filesystem;
    return optEC1(fs::hard_link_count, fs::hard_link_count);
}

inline FileSystem::FileClock::time_point FileSystem::fcModification() const
{
    namespace fs = std::filesystem;
    return optEC1(fs::last_write_time, fs::last_write_time);
}

inline FileSystem::SystemClock::time_point FileSystem::modification() const
{
    return FileClock::to_sys(fcModification());
}

inline FileSystem FileSystem::destination() const
{
    namespace fs = std::filesystem;
    return FileSystem{ optEC1(fs::read_symlink, fs::read_symlink) };
}

inline bool FileSystem::remove() const
{
    namespace fs = std::filesystem;
    return optEC1(fs::remove, fs::remove);
}    

inline std::uintmax_t FileSystem::removeAll() const
{
    namespace fs = std::filesystem;
    return optEC1(fs::remove_all, fs::remove_all);
}    

inline bool FileSystem::rename(Path const &newName) const
{
    namespace fs = std::filesystem;
    return optEC3(newName, fs::rename, fs::rename);
}
    
inline bool FileSystem::rename(FileSystem const &newName) const
{
    return rename(newName.d_path);
}
    
// static
inline FileSystem FileSystem::tmpDir(EC &ec)
{
    return FileSystem{ std::filesystem::temp_directory_path(ec) };
}

inline FileSystem &FileSystem::setExtension(std::string const &ext)
{
    d_path.replace_extension(ext);
    return *this;
}

inline FileSystem &FileSystem::setFilename(std::string const &newName)
{
    d_path.replace_filename(newName);
    return *this;
}

inline bool FileSystem::knownStatus() const
{
    namespace fs = std::filesystem;
    return fs::status(d_path, s_errorCode) != fs::file_status{};
}

inline FileSystem::FileType FileSystem::type(bool destination) const
{
    return status(destination).type();
}

inline FileSystem::Perms FileSystem::permissions() const
{
    return status().permissions();
}

inline FileSystem::DirEntry FileSystem::directory() const
{
    namespace fs = std::filesystem;
    return fs::directory_entry(d_path);
}


template <typename PermType>
inline FileSystem const &FileSystem::setPermissions(PermType perms, 
                                                    FSOptions opt) const
{
    return setPerms(static_cast<Perms>(perms), opt);
}

template <typename PermType>
inline FileSystem &FileSystem::setPermissions(PermType perms, FSOptions opt)
{
    return setPerms(static_cast<Perms>(perms), opt);
}

// static
inline std::error_code &FileSystem::errorCode()
{
    return s_errorCode;
}
