summaryrefslogtreecommitdiffstats
path: root/src/track/trackref.h
blob: 4288a0c669bee55cd928ff80f14bca89ef21a1db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#pragma once

#include "track/trackid.h"
#include "util/fileinfo.h"

// A track in the library is identified by a location and an id.
// The location is mandatory to identify the file, whereas the id
// only exists after the track has been inserted into the database.
//
// This class is intended to be used as a simple, almost immutable
// value object. Only the id can be set once.
class TrackRef final {
  public:
    /// Converts a file path and an optional TrackId into a TrackRef.
    ///
    /// This involves and intermediate creation of mixxx::FileInfo
    /// and accessing the file system!
    static TrackRef fromFilePath(
            const QString& filePath,
            TrackId id = TrackId()) {
        return fromFileInfo(mixxx::FileInfo(filePath), id);
    }

    /// Converts a mixxx::FileInfo and an optional TrackId into a TrackRef.
    ///
    /// This involves obtaining the file-related track properties from
    /// the file info and might involve accessing the file system!
    static TrackRef fromFileInfo(
            mixxx::FileInfo fileInfo,
            TrackId id = TrackId()) {
        // The conditional refresh ensures that files that were previously
        // unavailable (e.g. file system volume not mounted before) are
        // resolved successfully.
        auto canonicalLocation = fileInfo.resolveCanonicalLocation();
        // All properties of the file info are now considered fresh
        return TrackRef(
                fileInfo.location(),
                std::move(canonicalLocation),
                std::move(id));
    }

    // Default constructor
    TrackRef() {
        DEBUG_ASSERT(verifyConsistency());
    }
    // Regular copy constructor
    TrackRef(const TrackRef& other) = default;
    // Custom copy constructor:  Creates a copy of an existing TrackRef,
    // but overwrite the TrackId with a custom value.
    TrackRef(
            const TrackRef& other,
            TrackId id)
        : m_location(other.m_location),
          m_canonicalLocation(other.m_canonicalLocation),
          m_id(id) {
        DEBUG_ASSERT(verifyConsistency());
    }

    // The human-readable identifier of a track in Mixxx. The location is
    // immutable and the starting point for accessing a track's file.
    const QString& getLocation() const {
        return m_location;
    }
    bool hasLocation() const {
        return !getLocation().isEmpty();
    }

    // The unique identifier of a track's file at runtime and used
    // for caching. The canonical location is empty for inexistent
    // files.
    const QString& getCanonicalLocation() const {
        return m_canonicalLocation;
    }
    bool hasCanonicalLocation() const {
        return !getCanonicalLocation().isEmpty();
    }

    // The primary key of a track in the Mixxx library. The id must only
    // be set once after inserting into or after loading from the database.
    // Tracks that have not been stored in the database don't have an id.
    const TrackId& getId() const {
        return m_id;
    }
    bool hasId() const {
        return getId().isValid();
    }

    bool isValid() const {
        return hasId() || hasCanonicalLocation();
    }

protected:
    // Initializing constructor
    TrackRef(
            const QString& location,
            const QString& canonicalLocation,
            TrackId id = TrackId())
        : m_location(location),
          m_canonicalLocation(canonicalLocation),
          m_id(id) {
        DEBUG_ASSERT(verifyConsistency());
    }

private:
    // Checks if all class invariants are met
    bool verifyConsistency() const;

    QString m_location;
    QString m_canonicalLocation;
    TrackId m_id;
};

inline
bool operator==(const TrackRef& lhs, const TrackRef& rhs) {
    return (lhs.getId() == rhs.getId()) &&
            (lhs.getLocation() == rhs.getLocation()) &&
            (lhs.getCanonicalLocation() == rhs.getCanonicalLocation());
}

inline
bool operator!=(const TrackRef& lhs, const TrackRef& rhs) {
    return !(lhs == rhs);
}

Q_DECLARE_METATYPE(TrackRef)

std::ostream& operator<<(std::ostream& os, const TrackRef& trackRef);

QDebug operator<<(QDebug debug, const TrackRef& trackRef);

inline uint qHash(
        const TrackRef& key,
        uint seed = 0) {
    return qHash(
            key.getLocation(), seed) ^
            qHash(key.getId(), seed);
}