1 /* 2 * LibrePCB - Professional EDA for everyone! 3 * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors. 4 * https://librepcb.org/ 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #ifndef LIBREPCB_TRANSACTIONALFILESYSTEM_H 21 #define LIBREPCB_TRANSACTIONALFILESYSTEM_H 22 23 /******************************************************************************* 24 * Includes 25 ******************************************************************************/ 26 #include "directorylock.h" 27 #include "filesystem.h" 28 29 #include <QtCore> 30 31 #include <memory> 32 33 /******************************************************************************* 34 * Namespace / Forward Declarations 35 ******************************************************************************/ 36 37 class QuaZipFile; 38 39 namespace librepcb { 40 41 /******************************************************************************* 42 * Class TransactionalFileSystem 43 ******************************************************************************/ 44 45 /** 46 * @brief Transactional ::librepcb::FileSystem implementation 47 * 48 * This is an implementation of the ::librepcb::FileSystem interface with many 49 * features needed to create, open and save LibrePCB library elements and 50 * projects in a very safe way to always guarantee consistency of all files. 51 * 52 * It handles following things: 53 * - Supports read-only access to the file system to guarantee absolutely 54 * nothing is written to the disk. 55 * - In R/W mode, it locks the accessed directory to avoid parallel usage (see 56 * @ref doc_project_lock) 57 * - Supports periodic saving to allow restoring the last autosave backup after 58 * an application crash (see @ref doc_project_autosave). 59 * - Holds all file modifications in memory and allows to write those in an 60 * atomic way to the disk (see @ref doc_project_save). 61 * - Allows to export the whole file system to a ZIP file. 62 */ 63 class TransactionalFileSystem final : public FileSystem { 64 Q_OBJECT 65 66 public: 67 /** 68 * @brief Function to filter files 69 * 70 * @param filePath The relative file path to filter. 71 * 72 * @retval true Include file. 73 * @retval false Do not include file. 74 */ 75 typedef std::function<bool(const QString& filePath)> FilterFunction; 76 77 /** 78 * @brief Callback type used to determine whether a backup should be restored 79 * or not 80 * 81 * @param dir The directory to be restored. 82 * 83 * @retval true Restore backup. 84 * @retval false Do not restore backup. 85 * 86 * @throw ::librepcb::Exception to abort opening the directory. 87 */ 88 typedef std::function<bool(const FilePath& dir)> RestoreCallback; 89 90 /** 91 * @brief Convenience class providing standard implementations for 92 * ::librepcb::TransactionalFileSystem::RestoreCallback 93 * 94 * @note This is a class just to put some functions into their own scope. 95 */ 96 struct RestoreMode { 97 /** 98 * @brief Never restore a backup 99 * 100 * @param dir The directory to be opened. 101 * 102 * @return false 103 */ noRestoreMode104 static bool no(const FilePath& dir) { 105 Q_UNUSED(dir); 106 return false; 107 } 108 109 /** 110 * @brief Always restore the backup, if there is any 111 * 112 * @param dir The directory to be opened. 113 * 114 * @return true 115 */ yesRestoreMode116 static bool yes(const FilePath& dir) { 117 Q_UNUSED(dir); 118 return true; 119 } 120 121 /** 122 * @brief If there exists a backup, abort opening the directory by raising 123 * an exception 124 * 125 * @param dir The directory to be opened. 126 * 127 * @return Nothing, since an exception is thrown. 128 * 129 * @throw ::librepcb::RuntimeError 130 */ abortRestoreMode131 static bool abort(const FilePath& dir) { 132 throw RuntimeError(__FILE__, __LINE__, 133 QString("Autosave backup detected in directory '%1'.") 134 .arg(dir.toNative())); 135 } 136 }; 137 138 // Constructors / Destructor 139 TransactionalFileSystem() = delete; 140 TransactionalFileSystem( 141 const FilePath& filepath, bool writable = false, 142 RestoreCallback restoreCallback = RestoreCallback(), 143 DirectoryLock::LockHandlerCallback lockCallback = nullptr, 144 QObject* parent = nullptr); 145 TransactionalFileSystem(const TransactionalFileSystem& other) = delete; 146 virtual ~TransactionalFileSystem() noexcept; 147 148 // Getters getPath()149 const FilePath& getPath() const noexcept { return mFilePath; } isWritable()150 bool isWritable() const noexcept { return mIsWritable; } isRestoredFromAutosave()151 bool isRestoredFromAutosave() const noexcept { return mRestoredFromAutosave; } 152 153 // Inherited from FileSystem 154 virtual FilePath getAbsPath(const QString& path = "") const noexcept override; 155 virtual QStringList getDirs(const QString& path = "") const noexcept override; 156 virtual QStringList getFiles(const QString& path = "") const 157 noexcept override; 158 virtual bool fileExists(const QString& path) const noexcept override; 159 virtual QByteArray read(const QString& path) const override; 160 virtual void write(const QString& path, const QByteArray& content) override; 161 virtual void removeFile(const QString& path) override; 162 virtual void removeDirRecursively(const QString& path = "") override; 163 164 // General Methods 165 void loadFromZip(QByteArray content); 166 void loadFromZip(const FilePath& fp); 167 QByteArray exportToZip(FilterFunction filter = nullptr) const; 168 void exportToZip(const FilePath& fp, FilterFunction filter = nullptr) const; 169 void discardChanges() noexcept; 170 QStringList checkForModifications() const; 171 void autosave(); 172 void save(); 173 174 // Static Methods 175 static std::shared_ptr<TransactionalFileSystem> open( 176 const FilePath& filepath, bool writable, 177 RestoreCallback restoreCallback = &RestoreMode::no, 178 DirectoryLock::LockHandlerCallback lockCallback = nullptr, 179 QObject* parent = nullptr) { 180 return std::make_shared<TransactionalFileSystem>( 181 filepath, writable, restoreCallback, lockCallback, parent); 182 } 183 static std::shared_ptr<TransactionalFileSystem> openRO( 184 const FilePath& filepath, 185 RestoreCallback restoreCallback = &RestoreMode::no, 186 QObject* parent = nullptr) { 187 return open(filepath, false, restoreCallback, nullptr, parent); 188 } 189 static std::shared_ptr<TransactionalFileSystem> openRW( 190 const FilePath& filepath, 191 RestoreCallback restoreCallback = &RestoreMode::no, 192 DirectoryLock::LockHandlerCallback lockCallback = nullptr, 193 QObject* parent = nullptr) { 194 return open(filepath, true, restoreCallback, lockCallback, parent); 195 } 196 static QString cleanPath(QString path) noexcept; 197 198 private: // Methods 199 bool isRemoved(const QString& path) const noexcept; 200 void exportDirToZip(QuaZipFile& file, const FilePath& zipFp, 201 const QString& dir, FilterFunction filter) const; 202 void saveDiff(const QString& type) const; 203 void loadDiff(const FilePath& fp); 204 void removeDiff(const QString& type); 205 206 private: // Data 207 FilePath mFilePath; 208 bool mIsWritable; 209 DirectoryLock mLock; 210 bool mRestoredFromAutosave; 211 212 // File system modifications 213 QHash<QString, QByteArray> mModifiedFiles; 214 QSet<QString> mRemovedFiles; 215 QSet<QString> mRemovedDirs; 216 }; 217 218 /******************************************************************************* 219 * End of File 220 ******************************************************************************/ 221 222 } // namespace librepcb 223 224 #endif // LIBREPCB_TRANSACTIONALFILESYSTEM_H 225