mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
153 lines
4.7 KiB
C++
153 lines
4.7 KiB
C++
// Copyright 2023 Google LLC
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <exception>
|
|
#include <filesystem> // NOLINT(build/c++17)
|
|
#include <fstream>
|
|
#include <optional>
|
|
|
|
#include "internal/platform/implementation/linux/preferences_repository.h"
|
|
#include "internal/platform/logging.h"
|
|
#include "nlohmann/json.hpp"
|
|
#include "nlohmann/json_fwd.hpp"
|
|
|
|
namespace nearby {
|
|
namespace linux {
|
|
namespace {
|
|
using json = ::nlohmann::json;
|
|
|
|
constexpr char kPreferencesFileName[] = "preferences.json";
|
|
constexpr char kPreferencesBackupFileName[] = "preferences_bak.json";
|
|
|
|
} // namespace
|
|
|
|
json PreferencesRepository::LoadPreferences() {
|
|
absl::MutexLock lock(&mutex_);
|
|
std::optional<json> preferences = AttemptLoad();
|
|
if (preferences.has_value()) {
|
|
// The top level root should be an object, if it's not then something went
|
|
// wrong or the file was corrupted.
|
|
if (!preferences.value().is_object()) {
|
|
LOG(ERROR) << "Preferences loaded was not a valid object: "
|
|
<< preferences.value().dump(4);
|
|
|
|
return json::object();
|
|
}
|
|
|
|
return preferences.value();
|
|
}
|
|
|
|
LOG(ERROR) << "Could not load preferences file, trying backup.";
|
|
|
|
// In the future we should switch to using a transaction log or another
|
|
// stable method which doesn't pose a risk of losing settings
|
|
preferences = RestoreFromBackup();
|
|
if (preferences.has_value()) {
|
|
LOG(ERROR) << "Successfully recovered from backup.";
|
|
return preferences.value();
|
|
}
|
|
|
|
LOG(ERROR) << "Failed to load preferences file from back up.";
|
|
|
|
return json::object();
|
|
}
|
|
|
|
bool PreferencesRepository::SavePreferences(json preferences) {
|
|
absl::MutexLock lock(&mutex_);
|
|
try {
|
|
std::filesystem::path path = path_;
|
|
if (!std::filesystem::exists(path) &&
|
|
!std::filesystem::create_directories(path)) {
|
|
LOG(ERROR) << "Failed to create preferences path.";
|
|
return false;
|
|
}
|
|
|
|
std::filesystem::path full_name = path / kPreferencesFileName;
|
|
std::filesystem::path full_name_backup = path / kPreferencesBackupFileName;
|
|
|
|
// Create a backup without moving the bytes on disk
|
|
if (std::filesystem::exists(full_name)) {
|
|
LOG(INFO) << "Making backup of preferences file.";
|
|
std::filesystem::rename(full_name, full_name_backup);
|
|
}
|
|
|
|
std::ofstream preferences_file(full_name.c_str());
|
|
preferences_file << preferences;
|
|
preferences_file.close();
|
|
|
|
// Make sure the file wasn't saved in a corrupted state
|
|
if (!AttemptLoad().has_value()) {
|
|
LOG(ERROR) << "Preferences saved to disk in corrupted state. "
|
|
"Restoring from backup.";
|
|
|
|
if (!RestoreFromBackup().has_value()) {
|
|
LOG(ERROR) << "Failed to restore preferences file.";
|
|
return false;
|
|
}
|
|
}
|
|
} catch (const std::exception& e) {
|
|
LOG(ERROR) << "Failed to save preferences file: " << e.what();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::optional<json> PreferencesRepository::AttemptLoad() {
|
|
std::filesystem::path path = path_;
|
|
std::filesystem::path full_name = path / kPreferencesFileName;
|
|
if (!std::filesystem::exists(path) || !std::filesystem::exists(full_name)) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
try {
|
|
std::ifstream preferences_file(full_name.c_str());
|
|
if (!preferences_file.good()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
json preferences = json::parse(preferences_file, nullptr, false);
|
|
preferences_file.close();
|
|
|
|
if (preferences.is_discarded()) {
|
|
LOG(ERROR) << "Preferences file corrupted.";
|
|
return std::nullopt;
|
|
}
|
|
|
|
return preferences;
|
|
} catch (const std::exception& e) {
|
|
LOG(ERROR) << "Exception while loading preferences: " << e.what();
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<json> PreferencesRepository::RestoreFromBackup() {
|
|
std::filesystem::path path = path_;
|
|
std::filesystem::path full_name = path / kPreferencesFileName;
|
|
std::filesystem::path full_name_backup = path / kPreferencesBackupFileName;
|
|
|
|
if (!std::filesystem::exists(full_name_backup)) {
|
|
LOG(WARNING)
|
|
<< "Backup requested but no backup preferences file found.";
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::filesystem::rename(full_name_backup, full_name);
|
|
|
|
LOG(INFO) << "Attempting load from backup preferences.";
|
|
return AttemptLoad();
|
|
}
|
|
|
|
} // namespace linux
|
|
} // namespace nearby
|