You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

299 lines
6.8 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project
#ifndef MPD_DIRECTORY_HXX
#define MPD_DIRECTORY_HXX
#include "Ptr.hxx"
#include "Song.hxx" // TODO eliminate this include, forward-declare only
#include "db/Visitor.hxx"
#include "db/PlaylistVector.hxx"
#include "db/Ptr.hxx"
#include "util/IntrusiveList.hxx"
#include <string>
#include <string_view>
/**
* Virtual directory that is really an archive file or a folder inside
* the archive (special value for Directory::device).
*/
static constexpr unsigned DEVICE_INARCHIVE = -1;
/**
* Virtual directory that is really a song file with one or more "sub"
* songs as specified by DecoderPlugin::container_scan() (special
* value for Directory::device).
*/
static constexpr unsigned DEVICE_CONTAINER = -2;
/**
* Virtual directory that is really a playlist file (special value for
* Directory::device).
*/
static constexpr unsigned DEVICE_PLAYLIST = -3;
class SongFilter;
struct Directory : IntrusiveListHook<> {
/* Note: the #IntrusiveListHook is protected with the global
#db_mutex. Read access in the update thread does not need
protection. */
using List = IntrusiveList<Directory>;
/**
* A doubly linked list of child directories.
*
* This attribute is protected with the global #db_mutex.
* Read access in the update thread does not need protection.
*/
List children;
/**
* A doubly linked list of songs within this directory.
*
* This attribute is protected with the global #db_mutex.
* Read access in the update thread does not need protection.
*/
IntrusiveList<Song> songs;
PlaylistVector playlists;
Directory *const parent;
std::chrono::system_clock::time_point mtime =
std::chrono::system_clock::time_point::min();
uint64_t inode = 0, device = 0;
const std::string path;
/**
* If this is not nullptr, then this directory does not really
* exist, but is a mount point for another #Database.
*/
DatabasePtr mounted_database;
/**
* This field is used by the database update to check whether
* an item has disappeared.
*/
bool mark;
public:
Directory(std::string &&_path_utf8, Directory *_parent) noexcept;
~Directory() noexcept;
/**
* Create a new root #Directory object.
*/
[[gnu::malloc]] [[gnu::returns_nonnull]]
static Directory *NewRoot() noexcept {
return new Directory(std::string(), nullptr);
}
bool IsPlaylist() const noexcept {
return device == DEVICE_PLAYLIST;
}
/**
* Is this really a regular file which is being treated like a
* directory?
*/
bool IsReallyAFile() const noexcept {
return device == DEVICE_INARCHIVE ||
IsPlaylist() ||
device == DEVICE_CONTAINER;
}
bool IsMount() const noexcept {
return mounted_database != nullptr;
}
/**
* Checks whether this is a "special" directory
* (e.g. #DEVICE_PLAYLIST) and whether the underlying plugin
* is available.
*/
[[gnu::pure]]
bool IsPluginAvailable() const noexcept;
/**
* Remove this #Directory object from its parent and free it. This
* must not be called with the root Directory.
*
* Caller must lock the #db_mutex.
*/
void Delete() noexcept;
/**
* Create a new #Directory object as a child of the given one.
*
* Caller must lock the #db_mutex.
*
* @param name_utf8 the UTF-8 encoded name of the new sub directory
*/
Directory *CreateChild(std::string_view name_utf8) noexcept;
/**
* Caller must lock the #db_mutex.
*/
[[gnu::pure]]
const Directory *FindChild(std::string_view name) const noexcept;
[[gnu::pure]]
Directory *FindChild(std::string_view name) noexcept {
const Directory *cthis = this;
return const_cast<Directory *>(cthis->FindChild(name));
}
/**
* Look up a sub directory, and create the object if it does not
* exist.
*
* Caller must lock the #db_mutex.
*/
Directory *MakeChild(std::string_view name_utf8) noexcept {
Directory *child = FindChild(name_utf8);
if (child == nullptr)
child = CreateChild(name_utf8);
return child;
}
struct LookupResult {
/**
* The last directory that was found. If the given
* URI could not be resolved at all, then this is the
* root directory.
*/
Directory *directory;
/**
* The URI part which resolved to the #directory.
*/
std::string_view uri;
/**
* The remaining URI part (without leading slash) or
* empty if the given URI was consumed completely.
*/
std::string_view rest;
};
/**
* Looks up a directory by its relative URI.
*
* @param uri the relative URI
*/
[[gnu::pure]]
LookupResult LookupDirectory(std::string_view uri) noexcept;
[[gnu::pure]]
Song *LookupTargetSong(std::string_view target) noexcept;
[[gnu::pure]]
bool IsEmpty() const noexcept {
return children.empty() &&
songs.empty() &&
playlists.empty();
}
[[gnu::pure]]
const char *GetPath() const noexcept {
return path.c_str();
}
/**
* Returns the base name of the directory.
*/
[[gnu::pure]]
std::string_view GetName() const noexcept;
/**
* Is this the root directory of the music database?
*/
[[gnu::pure]]
bool IsRoot() const noexcept {
return parent == nullptr;
}
template<typename T>
void ForEachChildSafe(T &&t) {
const auto end = children.end();
for (auto i = children.begin(), next = i; i != end; i = next) {
next = std::next(i);
t(*i);
}
}
template<typename T>
void ForEachSongSafe(T &&t) {
const auto end = songs.end();
for (auto i = songs.begin(), next = i; i != end; i = next) {
next = std::next(i);
t(*i);
}
}
/**
* Look up a song in this directory by its name.
*
* Caller must lock the #db_mutex.
*/
[[gnu::pure]]
const Song *FindSong(std::string_view name_utf8) const noexcept;
[[gnu::pure]]
Song *FindSong(std::string_view name_utf8) noexcept {
const Directory *cthis = this;
return const_cast<Song *>(cthis->FindSong(name_utf8));
}
/**
* Add a song object to this directory. Its "parent" attribute must
* be set already.
*/
void AddSong(SongPtr song) noexcept;
/**
* Remove a song object from this directory (which effectively
* invalidates the song object, because the "parent" attribute becomes
* stale), and return ownership to the caller.
*/
SongPtr RemoveSong(Song *song) noexcept;
/**
* Recursively walk through the whole tree and set all
* `Song::in_playlist` fields to `false`.
*
* Caller must lock the #db_mutex.
*/
void ClearInPlaylist() noexcept;
/**
* Caller must lock the #db_mutex.
*/
void PruneEmpty() noexcept;
/**
* Sort all directory entries recursively.
*
* Caller must lock the #db_mutex.
*/
void Sort() noexcept;
/**
* Caller must lock #db_mutex.
*/
void Walk(bool recursive, const SongFilter *match,
bool hide_playlist_targets,
const VisitDirectory& visit_directory, const VisitSong& visit_song,
const VisitPlaylist& visit_playlist) const;
[[gnu::pure]]
LightDirectory Export() const noexcept;
};
#endif