aboutsummaryrefslogtreecommitdiffstats
path: root/util/folder/path.h
blob: e1615a947ab6efbe06d3ca72a7dba1df24ac4117 (plain) (blame)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#pragma once

#include "fwd.h"
#include "pathsplit.h"

#include <util/generic/ptr.h>
#include <util/generic/strbuf.h>
#include <util/generic/string.h>
#include <util/generic/vector.h>
#include <util/string/cast.h>
#include <util/system/fstat.h>
#include <util/system/platform.h>
#include <util/system/sysstat.h>
#include <util/system/yassert.h>

#include <utility>

/**
 * Class behaviour is platform-dependent.
 * It uses platform-dependent separators for path-reconstructing operations.
 */
class TFsPath {
private:
    struct TSplit;

public:
    TFsPath();
    TFsPath(const TString& path);
    TFsPath(const TStringBuf path);
    TFsPath(const char* path);

    TFsPath(const std::string& path)
        : TFsPath(TStringBuf(path))
    {
    }

    TFsPath(const TFsPath& that);
    TFsPath(TFsPath&& that) = default;

    TFsPath& operator=(const TFsPath& that);
    TFsPath& operator=(TFsPath&& that) = default;

    ~TFsPath() = default;

    void CheckDefined() const;

    inline bool IsDefined() const {
        return Path_.length() > 0;
    }

    inline explicit operator bool() const {
        return IsDefined();
    }

    inline const char* c_str() const {
        return Path_.c_str();
    }

    inline operator const TString&() const {
        return Path_;
    }

    inline bool operator==(const TFsPath& that) const {
        return Path_ == that.Path_;
    }

    inline bool operator!=(const TFsPath& that) const {
        return Path_ != that.Path_;
    }

    TFsPath& operator/=(const TFsPath& that);

    friend TFsPath operator/(const TFsPath& s, const TFsPath& p) {
        TFsPath ret(s);
        return ret /= p;
    }

    const TPathSplit& PathSplit() const;

    TFsPath& Fix();

    inline const TString& GetPath() const {
        return Path_;
    }

    /// last component of path, or "/" if root
    TString GetName() const;

    /**
     * "a.b.tmp" -> "tmp"
     * "a.tmp"   -> "tmp"
     * ".tmp"    -> ""
     */
    TString GetExtension() const;

    bool IsAbsolute() const;
    bool IsRelative() const;

    /**
     * TFsPath("/a/b").IsSubpathOf("/a")        -> true
     *
     * TFsPath("/a").IsSubpathOf("/a")          -> false
     *
     * TFsPath("/a").IsSubpathOf("/other/path") -> false
     * @param that - presumable parent path of this
     * @return True if this is a subpath of that and false otherwise.
     */
    bool IsSubpathOf(const TFsPath& that) const;

    /**
     * TFsPath("/a/b").IsNonStrictSubpathOf("/a")        -> true
     *
     * TFsPath("/a").IsNonStrictSubpathOf("/a")          -> true
     *
     * TFsPath("/a").IsNonStrictSubpathOf("/other/path") -> false
     * @param that - presumable parent path of this
     * @return True if this is a subpath of that or they are equivalent and false otherwise.
     */
    bool IsNonStrictSubpathOf(const TFsPath& that) const;

    bool IsContainerOf(const TFsPath& that) const {
        return that.IsSubpathOf(*this);
    }

    TFsPath RelativeTo(const TFsPath& root) const; // must be subpath of root

    /**
     * @returns relative path or empty path if root equals to this.
     */
    TFsPath RelativePath(const TFsPath& root) const; //..; for relative paths 1st component must be the same

    /**
     * Never fails. Returns this if already a root.
     */
    TFsPath Parent() const;

    TString Basename() const {
        return GetName();
    }
    TString Dirname() const {
        return Parent();
    }

    TFsPath Child(const TString& name) const;

    /**
     * @brief create this directory
     *
     * @param mode specifies permissions to use as described in mkdir(2), makes sense only on Unix-like systems.
     *
     * Nothing to do if dir exists.
     */
    void MkDir(const int mode = MODE0777) const;

    /**
     * @brief create this directory and all parent directories as needed
     *
     * @param mode specifies permissions to use as described in mkdir(2), makes sense only on Unix-like systems.
     */
    void MkDirs(const int mode = MODE0777) const;

    // XXX: rewrite to return iterator
    void List(TVector<TFsPath>& children) const;
    void ListNames(TVector<TString>& children) const;

    // Check, if path contains at least one component with a specific name.
    bool Contains(const TString& component) const;

    // fails to delete non-empty directory
    void DeleteIfExists() const;
    // delete recursively. Does nothing if not exists
    void ForceDelete() const;

    // XXX: ino

    inline bool Stat(TFileStat& stat) const {
        stat = TFileStat(Path_.data());

        return stat.Mode;
    }

    bool Exists() const;
    /// false if not exists
    bool IsDirectory() const;
    /// false if not exists
    bool IsFile() const;
    /// false if not exists
    bool IsSymlink() const;
    /// throw TIoException if not exists
    void CheckExists() const;

    void RenameTo(const TString& newPath) const;
    void RenameTo(const char* newPath) const;
    void RenameTo(const TFsPath& newFile) const;
    void ForceRenameTo(const TString& newPath) const;

    void CopyTo(const TString& newPath, bool force) const;

    void Touch() const;

    TFsPath RealPath() const;
    TFsPath RealLocation() const;
    TFsPath ReadLink() const;

    /// always absolute
    static TFsPath Cwd();

    inline void Swap(TFsPath& p) noexcept {
        DoSwap(Path_, p.Path_);
        Split_.Swap(p.Split_);
    }

private:
    void InitSplit() const;
    TSplit& GetSplit() const;
    void CopySplitFrom(const TFsPath& that) const;

private:
    TString Path_;
    /// cache
    mutable TSimpleIntrusivePtr<TSplit> Split_;
};

namespace NPrivate {
    inline void AppendToFsPath(TFsPath&) {
    }

    template <class T, class... Ts>
    void AppendToFsPath(TFsPath& fsPath, const T& arg, Ts&&... args) {
        fsPath /= TFsPath(arg);
        AppendToFsPath(fsPath, std::forward<Ts>(args)...);
    }
}

template <class... Ts>
TString JoinFsPaths(Ts&&... args) {
    TFsPath fsPath;
    ::NPrivate::AppendToFsPath(fsPath, std::forward<Ts>(args)...);
    return fsPath.GetPath();
}