1 /*
2 * FileChangeEvent.hpp
3 *
4 * Copyright (C) 2021 by RStudio, PBC
5 *
6 * Unless you have received this program directly from RStudio pursuant
7 * to the terms of a commercial license agreement with RStudio, then
8 * this program is licensed to you under the terms of version 3 of the
9 * GNU Affero General Public License. This program is distributed WITHOUT
10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13 *
14 */
15
16 #ifndef CORE_SYSTEM_FILE_CHANGE_EVENT_HPP
17 #define CORE_SYSTEM_FILE_CHANGE_EVENT_HPP
18
19 #include <string>
20 #include <iostream>
21 #include <vector>
22
23 #include <boost/function.hpp>
24
25 #include <core/FileInfo.hpp>
26
27 namespace rstudio {
28 namespace core {
29
30 class Error;
31
32 namespace system {
33
34 // event struct
35 class FileChangeEvent
36 {
37 public:
38 // NOTE: skip 2 for compatibility with old clients (used to be FileRenamed)
39 enum Type
40 {
41 None = 0,
42 FileAdded = 1,
43 FileRemoved = 3,
44 FileModified = 4
45 };
46
47 public:
FileChangeEvent(Type type,const core::FileInfo & fileInfo)48 FileChangeEvent(Type type, const core::FileInfo& fileInfo)
49 : type_(type), fileInfo_(fileInfo)
50 {
51 }
52
53 // COPYING: via compiler
54
operator =(const FileChangeEvent & rhs)55 FileChangeEvent& operator=(const FileChangeEvent& rhs)
56 {
57 if (&rhs != this)
58 {
59 type_ = rhs.type_;
60 fileInfo_ = rhs.fileInfo_;
61 }
62 return *this;
63 }
64
65 public:
type() const66 Type type() const { return type_; }
fileInfo() const67 const FileInfo& fileInfo() const { return fileInfo_; }
68
69 private:
70 Type type_;
71 core::FileInfo fileInfo_;
72 };
73
operator <<(std::ostream & ostr,const FileChangeEvent & event)74 inline std::ostream& operator << (std::ostream& ostr,
75 const FileChangeEvent& event)
76 {
77 if (event.type() == FileChangeEvent::FileAdded)
78 ostr << "FileAdded: ";
79 else if (event.type() == FileChangeEvent::FileRemoved)
80 ostr << "FileRemoved: ";
81 else if (event.type() == FileChangeEvent::FileModified)
82 ostr << "FileModified: ";
83
84 ostr << event.fileInfo();
85
86 if (event.fileInfo().isDirectory())
87 ostr << " (directory)";
88
89 return ostr;
90 }
91
92 template<typename PreviousIterator, typename CurrentIterator>
collectFileChangeEvents(PreviousIterator prevBegin,PreviousIterator prevEnd,CurrentIterator currBegin,CurrentIterator currEnd,const boost::function<bool (const FileInfo &)> & filter,std::vector<FileChangeEvent> * pEvents)93 void collectFileChangeEvents(PreviousIterator prevBegin,
94 PreviousIterator prevEnd,
95 CurrentIterator currBegin,
96 CurrentIterator currEnd,
97 const boost::function<bool(const FileInfo&)>& filter,
98 std::vector<FileChangeEvent>* pEvents)
99 {
100 // sort the ranges
101 std::vector<FileInfo> prev;
102 std::copy(prevBegin, prevEnd, std::back_inserter(prev));
103 std::sort(prev.begin(), prev.end(), fileInfoPathLessThan);
104 std::vector<FileInfo> curr;
105 std::copy(currBegin, currEnd, std::back_inserter(curr));
106 std::sort(curr.begin(), curr.end(), fileInfoPathLessThan);
107
108 // initalize the iterators
109 std::vector<FileInfo>::iterator prevIt = prev.begin();
110 std::vector<FileInfo>::iterator currIt = curr.begin();
111
112 FileInfo noFile;
113 while (prevIt != prev.end() || currIt != curr.end())
114 {
115 const FileInfo& prevFile = prevIt != prev.end() ? *prevIt : noFile;
116 const FileInfo& currFile = currIt != curr.end() ? *currIt : noFile;
117
118 int comp;
119 if (prevFile.empty())
120 comp = 1;
121 else if (currFile.empty())
122 comp = -1;
123 else
124 comp = fileInfoPathCompare(prevFile, currFile);
125
126 if (comp == 0)
127 {
128 if (currFile.lastWriteTime() != prevFile.lastWriteTime())
129 {
130 if (!filter || filter(currFile))
131 {
132 pEvents->push_back(FileChangeEvent(FileChangeEvent::FileModified,
133 currFile));
134 }
135 }
136 prevIt++;
137 currIt++;
138 }
139 else if (comp < 0)
140 {
141 if (!filter || filter(prevFile))
142 {
143 pEvents->push_back(FileChangeEvent(FileChangeEvent::FileRemoved,
144 prevFile));
145 }
146 prevIt++;
147 }
148 else // comp > 1
149 {
150 if (!filter || filter(currFile))
151 {
152 pEvents->push_back(FileChangeEvent(FileChangeEvent::FileAdded,
153 currFile));
154 }
155 currIt++;
156 }
157 }
158 }
159
160 template<typename PreviousIterator, typename CurrentIterator>
collectFileChangeEvents(PreviousIterator prevBegin,PreviousIterator prevEnd,CurrentIterator currBegin,CurrentIterator currEnd,std::vector<FileChangeEvent> * pEvents)161 void collectFileChangeEvents(PreviousIterator prevBegin,
162 PreviousIterator prevEnd,
163 CurrentIterator currBegin,
164 CurrentIterator currEnd,
165 std::vector<FileChangeEvent>* pEvents)
166 {
167 collectFileChangeEvents(prevBegin,
168 prevEnd,
169 currBegin,
170 currEnd,
171 boost::function<bool(const FileInfo&)>(),
172 pEvents);
173 }
174
175 } // namespace system
176 } // namespace core
177 } // namespace rstudio
178
179 #endif // CORE_SYSTEM_FILE_CHANGE_EVENT_HPP
180
181
182