/
SettingsManager.h
147 lines (124 loc) · 5.57 KB
/
SettingsManager.h
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
#pragma once
#include <string>
#include "version.h"
#include "imodule.h"
#include "MajorMinorVersion.h"
#include "os/dir.h"
#include "os/path.h"
namespace settings
{
/**
* Settings file manager, dealing with saving and loading settings files
* to the user's settings path. DarkRadiant 2.15+ or higher stores its settings
* in folders specific to their major and minor versions:
*
* $HOME/.config/darkradiant/2.15/
* $HOME/.config/darkradiant/3.0/
*
* When loading settings files, this manager class will search the config folders
* of the current version and all previous versions, making it possible to use
* or upgrade settings saved by older versions. The most recent versions will
* take precedence over older files.
* It will ignore any files stored in higher or future version folders.
* For backwards compatibility with DarkRadiant versions that have not been
* using the version-specific settings folders, files in the base settings path
* will be considered too, with the lowest priority.
*
* When saving settings files, there's only a single target folder, which is
* the one corresponding to the current application version.
*/
class SettingsManager
{
private:
const IApplicationContext& _context;
MajorMinorVersion _currentVersion;
// The path where this version writes its settings files to
std::string _currentVersionSettingsFolder;
// All existing version folders found in the settings folder
std::set<MajorMinorVersion> _existingVersionFolders;
public:
// Construct a settings manager for this version of DarkRadiant
// Will create the settings path for this version if it's not existing yet.
SettingsManager(const IApplicationContext& context) :
SettingsManager(context, RADIANT_VERSION)
{}
SettingsManager(const SettingsManager& other) = delete;
SettingsManager& operator=(const SettingsManager& other) = delete;
// Construct a settings manager instance with a specific version
// string in the format "Major.Minor.Micro[BuildSuffix]", or "2.14.0pre1".
// Mainly used for unit test purposes, regular code should use the default constructor.
SettingsManager(const IApplicationContext& context, const std::string& currentVersion) :
_context(context),
_currentVersion(currentVersion)
{
// Set up the path to the current version
_currentVersionSettingsFolder = os::standardPathWithSlash(context.getSettingsPath() + _currentVersion.toString());
// Make sure the output folder exists
os::makeDirectory(_currentVersionSettingsFolder);
// Create an inventory of the existing version folders
checkExistingVersionFolders();
}
// Returns the output path (including trailing slash) where all settings files
// for the current version can be saved to.
// Matches the pattern IApplicationContext::getSettingsPath()/"major.minor"/
const std::string& getCurrentVersionSettingsFolder() const
{
return _currentVersionSettingsFolder;
}
/**
* Returns the full path to the first matching existing settings file (given by its path
* relative to the settings folder).
*
* This will search any settings folders for the current or any previous
* application version, returning the first matching file.
* The version folders are searched in descending order, with the current version having
* the highest priority. This way the file saved by the most recent known version
* will be picked, whereas versions saved by "future" application versions will be ignored.
*
* For compatibility reasons, files saved in the root settings folder will be
* considered too (with the lowest priority), as DarkRadiant versions prior to 2.15
* have not been creating version settings folders.
*
* Will return an empty string if no file was matching.
*/
std::string getExistingSettingsFile(const std::string& relativePath) const
{
fs::path settingsPath = _context.getSettingsPath();
// Go through the existing versions, starting from the highest (usually the current version)
for (auto v = _existingVersionFolders.rbegin(); v != _existingVersionFolders.rend(); ++v)
{
auto fullPath = settingsPath / v->toString() / relativePath;
if (fs::is_regular_file(fullPath))
{
return fullPath.string(); // found a match
}
}
// Finally, check the base folder if nothing else has been matching
auto fullPath = settingsPath / relativePath;
return fs::is_regular_file(fullPath) ? fullPath.string() : std::string();
}
private:
void checkExistingVersionFolders()
{
_existingVersionFolders.clear();
// Enumerate all existing folders and sort them by version
os::foreachItemInDirectory(_context.getSettingsPath(), [&](const fs::path& item)
{
// Skip non-directories
if (!fs::is_directory(item)) return;
try
{
// Attempt to parse the version from the folder name and insert into the list of
// existing versions. Due to operator< they will end up sorted in ascending order
MajorMinorVersion version(item.filename().string());
// Ignore all versions higher than the current one
if (_currentVersion < version) return;
// Sort this into the set
_existingVersionFolders.insert(version);
}
catch (const std::invalid_argument&)
{} // ignore invalid folder names
});
}
};
}