mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Replace WriteMode with MetadataWriteOptions
This is a breaking API change, but the addition of any future options won't be.
This commit is contained in:
+1
-1
@@ -49,7 +49,7 @@ categories = ["games"]
|
||||
ambiguous_negative_literals = "forbid"
|
||||
impl_trait_overcaptures = "forbid"
|
||||
let_underscore_drop = "deny"
|
||||
missing_copy_implementations = "forbid"
|
||||
missing_copy_implementations = "deny"
|
||||
missing_debug_implementations = "deny"
|
||||
non_ascii_idents = "forbid"
|
||||
redundant_imports = "forbid"
|
||||
|
||||
@@ -36,6 +36,97 @@
|
||||
#include "loot/metadata/plugin_metadata.h"
|
||||
|
||||
namespace loot {
|
||||
struct MetadataWriteOptionsImpl;
|
||||
|
||||
/** @brief Options to configure how metadata files are written. */
|
||||
class MetadataWriteOptions {
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a new set of options, all initially set to `false`.
|
||||
*/
|
||||
LOOT_API MetadataWriteOptions();
|
||||
LOOT_API MetadataWriteOptions(const MetadataWriteOptions&);
|
||||
LOOT_API MetadataWriteOptions(MetadataWriteOptions&&) noexcept;
|
||||
LOOT_API ~MetadataWriteOptions();
|
||||
|
||||
LOOT_API MetadataWriteOptions& operator=(const MetadataWriteOptions&);
|
||||
LOOT_API MetadataWriteOptions& operator=(MetadataWriteOptions&&) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Sets the option to overwrite the output file if it already exists.
|
||||
* @details If true and the file path already exists, its contents will be
|
||||
* replaced.
|
||||
*
|
||||
* If false and the file path already exists, an error will be
|
||||
* returned.
|
||||
*
|
||||
* This setting has no effect if the file path does not exist.
|
||||
* @param truncate
|
||||
* The value to set.
|
||||
*/
|
||||
LOOT_API void SetTruncate(bool truncate);
|
||||
|
||||
/**
|
||||
* @brief Sets the option to write YAML anchors and aliases.
|
||||
* @details If true then conditions, constraints, files, file details, plugin
|
||||
* cleaning data details, messages and message contents that appear
|
||||
* more than once in the metadata will be deduplicated by including
|
||||
* a YAML anchor when writing the first occurrence of the value, and
|
||||
* writing YAML aliases in place of further occurrences.
|
||||
*
|
||||
* If false, YAML anchors and aliases will not be used, so no
|
||||
* deduplication will occur.
|
||||
* @param writeAnchors
|
||||
* The value to set.
|
||||
*/
|
||||
LOOT_API void SetWriteAnchors(bool writeAnchors);
|
||||
|
||||
/**
|
||||
* @brief Sets the option to write YAML anchors in a `common` section.
|
||||
* @details If `writeAnchors` is true and this is also true, the document's
|
||||
* root-level map will start with a `common` key. Its value will be
|
||||
* a list of all the values for which YAML anchors will be written,
|
||||
* so that all YAML anchors will appear within that list.
|
||||
*
|
||||
* This setting has no effect if `writeAnchors` is false.
|
||||
* @param writeCommonSection
|
||||
* The value to set.
|
||||
*/
|
||||
LOOT_API void SetWriteCommonSection(bool writeCommonSection);
|
||||
|
||||
/**
|
||||
* @brief Sets the option to write anchors for File values that only have a
|
||||
* name.
|
||||
* @details If `writeAnchors` is true and this is also true, then all
|
||||
* repeated File metadata values will be deduplicated using YAML
|
||||
* anchors and aliases.
|
||||
*
|
||||
* If this is false, then only File metadata that is serialised as
|
||||
* a YAML object will be deduplicated (i.e. File values that only
|
||||
* have a name will not be deduplicated).
|
||||
*
|
||||
* This setting has no effect if `writeAnchors` is false.
|
||||
* @param anchorFileStrings
|
||||
* The value to set.
|
||||
*/
|
||||
LOOT_API void SetAnchorFileStrings(bool anchorFileStrings);
|
||||
|
||||
/** Gets the current value of the truncate option. */
|
||||
LOOT_API bool GetTruncate() const;
|
||||
|
||||
/** Gets the current value of the writeAnchors option. */
|
||||
LOOT_API bool GetWriteAnchors() const;
|
||||
|
||||
/** Gets the current value of the writeCommonSection option. */
|
||||
LOOT_API bool GetWriteCommonSection() const;
|
||||
|
||||
/** Gets the current value of the anchorFileStrings option. */
|
||||
LOOT_API bool GetAnchorFileStrings() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MetadataWriteOptionsImpl> pimpl_;
|
||||
};
|
||||
|
||||
/** @brief The interface provided by API's database handle. */
|
||||
class DatabaseInterface {
|
||||
public:
|
||||
@@ -98,7 +189,7 @@ public:
|
||||
* written. Otherwise, data will be written.
|
||||
*/
|
||||
virtual void WriteUserMetadata(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const = 0;
|
||||
const MetadataWriteOptions& options) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Writes a minimal metadata file that only contains plugins with
|
||||
@@ -111,7 +202,7 @@ public:
|
||||
* written. Otherwise, data will be written.
|
||||
*/
|
||||
virtual void WriteMinimalList(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const = 0;
|
||||
const MetadataWriteOptions& options) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Evaluate the given condition string.
|
||||
|
||||
@@ -4,7 +4,75 @@
|
||||
#include "api/convert.h"
|
||||
#include "api/exception/exception.h"
|
||||
|
||||
namespace {
|
||||
loot::MetadataWriteOptionsImpl convert(
|
||||
const loot::MetadataWriteOptions& options) {
|
||||
loot::MetadataWriteOptionsImpl output;
|
||||
|
||||
output.truncate = options.GetTruncate();
|
||||
output.write_anchors = options.GetWriteAnchors();
|
||||
output.write_common_section = options.GetWriteCommonSection();
|
||||
output.anchor_file_strings = options.GetAnchorFileStrings();
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
namespace loot {
|
||||
MetadataWriteOptions::MetadataWriteOptions() :
|
||||
pimpl_(std::make_unique<MetadataWriteOptionsImpl>()) {}
|
||||
|
||||
MetadataWriteOptions::MetadataWriteOptions(const MetadataWriteOptions& other) :
|
||||
pimpl_(std::make_unique<MetadataWriteOptionsImpl>(*other.pimpl_)) {}
|
||||
|
||||
MetadataWriteOptions::MetadataWriteOptions(
|
||||
MetadataWriteOptions&& other) noexcept :
|
||||
pimpl_(std::move(other).pimpl_) {}
|
||||
|
||||
MetadataWriteOptions::~MetadataWriteOptions() {}
|
||||
|
||||
MetadataWriteOptions& MetadataWriteOptions::operator=(
|
||||
const MetadataWriteOptions& other) {
|
||||
*pimpl_ = *other.pimpl_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MetadataWriteOptions& MetadataWriteOptions::operator=(
|
||||
MetadataWriteOptions&& other) noexcept {
|
||||
pimpl_ = std::move(other.pimpl_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MetadataWriteOptions::SetTruncate(bool truncate) {
|
||||
pimpl_->truncate = truncate;
|
||||
}
|
||||
|
||||
void MetadataWriteOptions::SetWriteAnchors(bool writeAnchors) {
|
||||
pimpl_->write_anchors = writeAnchors;
|
||||
}
|
||||
|
||||
void MetadataWriteOptions::SetWriteCommonSection(bool writeCommonSection) {
|
||||
pimpl_->write_common_section = writeCommonSection;
|
||||
}
|
||||
|
||||
void MetadataWriteOptions::SetAnchorFileStrings(bool anchorFileStrings) {
|
||||
pimpl_->anchor_file_strings = anchorFileStrings;
|
||||
}
|
||||
|
||||
bool MetadataWriteOptions::GetTruncate() const { return pimpl_->truncate; }
|
||||
|
||||
bool MetadataWriteOptions::GetWriteAnchors() const {
|
||||
return pimpl_->write_anchors;
|
||||
}
|
||||
|
||||
bool MetadataWriteOptions::GetWriteCommonSection() const {
|
||||
return pimpl_->write_common_section;
|
||||
}
|
||||
|
||||
bool MetadataWriteOptions::GetAnchorFileStrings() const {
|
||||
return pimpl_->anchor_file_strings;
|
||||
}
|
||||
|
||||
Database::Database(::rust::Box<loot::rust::Database>&& database) :
|
||||
database_(std::move(database)) {}
|
||||
|
||||
@@ -36,9 +104,9 @@ void Database::LoadUserlist(const std::filesystem::path& userlistPath) {
|
||||
}
|
||||
|
||||
void Database::WriteUserMetadata(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const {
|
||||
const MetadataWriteOptions& options) const {
|
||||
try {
|
||||
database_->write_user_metadata(outputFile.u8string(), overwrite);
|
||||
database_->write_user_metadata(outputFile.u8string(), ::convert(options));
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
@@ -209,9 +277,9 @@ void Database::DiscardAllUserMetadata() {
|
||||
}
|
||||
|
||||
void Database::WriteMinimalList(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const {
|
||||
const MetadataWriteOptions& options) const {
|
||||
try {
|
||||
database_->write_minimal_list(outputFile.u8string(), overwrite);
|
||||
database_->write_minimal_list(outputFile.u8string(), ::convert(options));
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
|
||||
@@ -10,19 +10,19 @@ class Database final : public DatabaseInterface {
|
||||
public:
|
||||
explicit Database(::rust::Box<loot::rust::Database>&& database);
|
||||
|
||||
void LoadMasterlist(const std::filesystem::path& masterlist_path) override;
|
||||
void LoadMasterlist(const std::filesystem::path& masterlistPath) override;
|
||||
|
||||
void LoadMasterlistWithPrelude(
|
||||
const std::filesystem::path& masterlist_path,
|
||||
const std::filesystem::path& masterlist_prelude_path) override;
|
||||
const std::filesystem::path& masterlistPath,
|
||||
const std::filesystem::path& masterlistPreludePath) override;
|
||||
|
||||
void LoadUserlist(const std::filesystem::path& userlist_path) override;
|
||||
|
||||
void WriteUserMetadata(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const override;
|
||||
const MetadataWriteOptions& options) const override;
|
||||
|
||||
void WriteMinimalList(const std::filesystem::path& outputFile,
|
||||
const bool overwrite) const override;
|
||||
const MetadataWriteOptions& options) const override;
|
||||
|
||||
bool Evaluate(const std::string& condition) const override;
|
||||
|
||||
|
||||
+6
-18
@@ -4,12 +4,12 @@ use std::{
|
||||
};
|
||||
|
||||
use delegate::delegate;
|
||||
use libloot::{EvalMode, MergeMode, WriteMode, error::DatabaseLockPoisonError};
|
||||
use libloot::{EvalMode, MergeMode, error::DatabaseLockPoisonError};
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
|
||||
use crate::{
|
||||
OptionalPluginMetadata, VerboseError,
|
||||
ffi::EdgeType,
|
||||
ffi::{EdgeType, MetadataWriteOptionsImpl},
|
||||
metadata::{Group, Message, PluginMetadata, to_vec_of_unwrapped},
|
||||
};
|
||||
|
||||
@@ -53,36 +53,24 @@ impl Database {
|
||||
pub fn write_user_metadata(
|
||||
&self,
|
||||
output_path: &str,
|
||||
overwrite: bool,
|
||||
options: MetadataWriteOptionsImpl,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_user_metadata(Path::new(output_path), write_mode)
|
||||
.write_user_metadata(Path::new(output_path), &options.into())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn write_minimal_list(
|
||||
&self,
|
||||
output_path: &str,
|
||||
overwrite: bool,
|
||||
options: MetadataWriteOptionsImpl,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_minimal_list(Path::new(output_path), write_mode)
|
||||
.write_minimal_list(Path::new(output_path), &options.into())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
||||
+32
-4
@@ -17,7 +17,7 @@ mod plugin;
|
||||
|
||||
use database::{Database, Vertex, new_vertex};
|
||||
use error::{EmptyOptionalError, VerboseError};
|
||||
use ffi::OptionalMessageContentRef;
|
||||
use ffi::{MetadataWriteOptionsImpl, OptionalMessageContentRef};
|
||||
use game::{Game, new_game, new_game_with_local_path};
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
use metadata::{
|
||||
@@ -112,6 +112,18 @@ impl TryFrom<ffi::LogLevel> for libloot::LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetadataWriteOptionsImpl> for libloot::MetadataWriteOptions {
|
||||
fn from(value: MetadataWriteOptionsImpl) -> Self {
|
||||
let mut options = libloot::MetadataWriteOptions::new();
|
||||
options.set_truncate(value.truncate);
|
||||
options.set_write_anchors(value.write_anchors);
|
||||
options.set_write_common_section(value.write_common_section);
|
||||
options.set_anchor_file_strings(value.anchor_file_strings);
|
||||
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(
|
||||
let_underscore_drop,
|
||||
missing_debug_implementations,
|
||||
@@ -123,7 +135,6 @@ impl TryFrom<ffi::LogLevel> for libloot::LogLevel {
|
||||
)]
|
||||
#[cxx::bridge(namespace = "loot::rust")]
|
||||
mod ffi {
|
||||
|
||||
pub enum GameType {
|
||||
Oblivion,
|
||||
Skyrim,
|
||||
@@ -181,6 +192,15 @@ mod ffi {
|
||||
pointer: *const MessageContent,
|
||||
}
|
||||
|
||||
#[namespace = "loot"]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct MetadataWriteOptionsImpl {
|
||||
truncate: bool,
|
||||
write_anchors: bool,
|
||||
write_common_section: bool,
|
||||
anchor_file_strings: bool,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
pub fn is_some(self: &OptionalMessageContentRef) -> bool;
|
||||
|
||||
@@ -260,9 +280,17 @@ mod ffi {
|
||||
|
||||
pub fn load_userlist(&self, path: &str) -> Result<()>;
|
||||
|
||||
pub fn write_user_metadata(&self, output_path: &str, overwrite: bool) -> Result<()>;
|
||||
pub fn write_user_metadata(
|
||||
&self,
|
||||
output_path: &str,
|
||||
options: MetadataWriteOptionsImpl,
|
||||
) -> Result<()>;
|
||||
|
||||
pub fn write_minimal_list(&self, output_path: &str, overwrite: bool) -> Result<()>;
|
||||
pub fn write_minimal_list(
|
||||
&self,
|
||||
output_path: &str,
|
||||
options: MetadataWriteOptionsImpl,
|
||||
) -> Result<()>;
|
||||
|
||||
pub fn evaluate(&self, condition: &str) -> Result<bool>;
|
||||
|
||||
|
||||
@@ -198,46 +198,55 @@ TEST_P(DatabaseInterfaceTest, loadUserlistShouldSucceedIfTheUserlistIsPresent) {
|
||||
TEST_P(
|
||||
DatabaseInterfaceTest,
|
||||
writeUserMetadataShouldThrowIfTheFileAlreadyExistsAndTheOverwriteArgumentIsFalse) {
|
||||
ASSERT_NO_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, false));
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().WriteUserMetadata(
|
||||
minimalOutputPath_, MetadataWriteOptions()));
|
||||
ASSERT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
|
||||
EXPECT_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, false),
|
||||
std::runtime_error);
|
||||
EXPECT_THROW(handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_,
|
||||
MetadataWriteOptions()),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
DatabaseInterfaceTest,
|
||||
writeUserMetadataShouldReturnOkAndWriteToFileIfTheArgumentsAreValidAndTheOverwriteArgumentIsTrue) {
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, true));
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, options));
|
||||
EXPECT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
DatabaseInterfaceTest,
|
||||
writeUserMetadataShouldReturnOkIfTheFileAlreadyExistsAndTheOverwriteArgumentIsTrue) {
|
||||
ASSERT_NO_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, false));
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().WriteUserMetadata(
|
||||
minimalOutputPath_, MetadataWriteOptions()));
|
||||
ASSERT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, true));
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, options));
|
||||
}
|
||||
|
||||
TEST_P(DatabaseInterfaceTest,
|
||||
writeUserMetadataShouldThrowIfPathGivenExistsAndIsReadOnly) {
|
||||
ASSERT_NO_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, false));
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().WriteUserMetadata(
|
||||
minimalOutputPath_, MetadataWriteOptions()));
|
||||
ASSERT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
|
||||
std::filesystem::permissions(minimalOutputPath_,
|
||||
std::filesystem::perms::owner_read,
|
||||
std::filesystem::perm_options::replace);
|
||||
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, true),
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, options),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
@@ -246,8 +255,11 @@ TEST_P(DatabaseInterfaceTest,
|
||||
ASSERT_NO_THROW(GenerateMasterlist());
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().LoadMasterlist(masterlistPath));
|
||||
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, true));
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, options));
|
||||
|
||||
EXPECT_EQ("{}", GetFileContent(minimalOutputPath_));
|
||||
}
|
||||
@@ -263,8 +275,11 @@ TEST_P(DatabaseInterfaceTest, writeUserMetadataShouldShouldWriteUserMetadata) {
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().LoadMasterlist(masterlistPath));
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().LoadUserlist(userlistPath_));
|
||||
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, true));
|
||||
handle_->GetDatabase().WriteUserMetadata(minimalOutputPath_, options));
|
||||
|
||||
EXPECT_FALSE(GetFileContent(minimalOutputPath_).empty());
|
||||
}
|
||||
@@ -899,54 +914,63 @@ TEST_P(
|
||||
|
||||
TEST_P(DatabaseInterfaceTest,
|
||||
writeMinimalListShouldReturnOkAndWriteToFileIfArgumentsGivenAreValid) {
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, false));
|
||||
EXPECT_NO_THROW(handle_->GetDatabase().WriteMinimalList(
|
||||
minimalOutputPath_, MetadataWriteOptions()));
|
||||
EXPECT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
DatabaseInterfaceTest,
|
||||
writeMinimalListShouldThrowIfTheFileAlreadyExistsAndTheOverwriteArgumentIsFalse) {
|
||||
ASSERT_NO_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, false));
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().WriteMinimalList(
|
||||
minimalOutputPath_, MetadataWriteOptions()));
|
||||
ASSERT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
|
||||
EXPECT_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, false),
|
||||
std::runtime_error);
|
||||
EXPECT_THROW(handle_->GetDatabase().WriteMinimalList(minimalOutputPath_,
|
||||
MetadataWriteOptions()),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
DatabaseInterfaceTest,
|
||||
writeMinimalListShouldReturnOkAndWriteToFileIfTheArgumentsAreValidAndTheOverwriteArgumentIsTrue) {
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, true));
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, options));
|
||||
EXPECT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
DatabaseInterfaceTest,
|
||||
writeMinimalListShouldReturnOkIfTheFileAlreadyExistsAndTheOverwriteArgumentIsTrue) {
|
||||
ASSERT_NO_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, false));
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().WriteMinimalList(
|
||||
minimalOutputPath_, MetadataWriteOptions()));
|
||||
ASSERT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, true));
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, options));
|
||||
}
|
||||
|
||||
TEST_P(DatabaseInterfaceTest,
|
||||
writeMinimalListShouldThrowIfPathGivenExistsAndIsReadOnly) {
|
||||
ASSERT_NO_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, false));
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().WriteMinimalList(
|
||||
minimalOutputPath_, MetadataWriteOptions()));
|
||||
ASSERT_TRUE(std::filesystem::exists(minimalOutputPath_));
|
||||
|
||||
std::filesystem::permissions(minimalOutputPath_,
|
||||
std::filesystem::perms::owner_read,
|
||||
std::filesystem::perm_options::replace);
|
||||
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, true),
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, options),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
@@ -957,8 +981,11 @@ TEST_P(DatabaseInterfaceTest,
|
||||
ASSERT_NO_THROW(GenerateMasterlist());
|
||||
ASSERT_NO_THROW(handle_->GetDatabase().LoadMasterlist(masterlistPath));
|
||||
|
||||
MetadataWriteOptions options;
|
||||
options.SetTruncate(true);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, true));
|
||||
handle_->GetDatabase().WriteMinimalList(minimalOutputPath_, options));
|
||||
|
||||
const auto content = GetFileContent(minimalOutputPath_);
|
||||
|
||||
|
||||
+58
-17
@@ -3,7 +3,7 @@ use std::{
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use libloot::{WriteMode, error::DatabaseLockPoisonError};
|
||||
use libloot::error::DatabaseLockPoisonError;
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
use napi_derive::napi;
|
||||
|
||||
@@ -44,6 +44,59 @@ impl From<MergeMode> for libloot::MergeMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MetadataWriteOptions(libloot::MetadataWriteOptions);
|
||||
|
||||
#[napi]
|
||||
impl MetadataWriteOptions {
|
||||
#[napi(constructor)]
|
||||
pub fn new() -> Self {
|
||||
MetadataWriteOptions(libloot::MetadataWriteOptions::new())
|
||||
}
|
||||
|
||||
#[napi(setter)]
|
||||
pub fn set_truncate(&mut self, truncate: bool) {
|
||||
self.0.set_truncate(truncate);
|
||||
}
|
||||
|
||||
#[napi(setter)]
|
||||
pub fn set_write_anchors(&mut self, write_anchors: bool) {
|
||||
self.0.set_write_anchors(write_anchors);
|
||||
}
|
||||
|
||||
#[napi(setter)]
|
||||
pub fn set_write_common_section(&mut self, write_common_section: bool) {
|
||||
self.0.set_write_common_section(write_common_section);
|
||||
}
|
||||
|
||||
#[napi(setter)]
|
||||
pub fn set_anchor_file_strings(&mut self, anchor_file_strings: bool) {
|
||||
self.0.set_anchor_file_strings(anchor_file_strings);
|
||||
}
|
||||
|
||||
#[napi(getter)]
|
||||
pub fn truncate(&self) -> bool {
|
||||
self.0.truncate()
|
||||
}
|
||||
|
||||
#[napi(getter)]
|
||||
pub fn write_anchors(&self) -> bool {
|
||||
self.0.write_anchors()
|
||||
}
|
||||
|
||||
#[napi(getter)]
|
||||
pub fn write_common_section(&self) -> bool {
|
||||
self.0.write_common_section()
|
||||
}
|
||||
|
||||
#[napi(getter)]
|
||||
pub fn anchor_file_strings(&self) -> bool {
|
||||
self.0.anchor_file_strings()
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Database(Arc<RwLock<libloot::Database>>);
|
||||
@@ -85,18 +138,12 @@ impl Database {
|
||||
pub fn write_user_metadata(
|
||||
&self,
|
||||
output_path: String,
|
||||
overwrite: bool,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_user_metadata(Path::new(&output_path), write_mode)
|
||||
.write_user_metadata(Path::new(&output_path), &options.0)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -104,18 +151,12 @@ impl Database {
|
||||
pub fn write_minimal_list(
|
||||
&self,
|
||||
output_path: String,
|
||||
overwrite: bool,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_minimal_list(Path::new(&output_path), write_mode)
|
||||
.write_minimal_list(Path::new(&output_path), &options.0)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
||||
+58
-17
@@ -3,7 +3,7 @@ use std::{
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use libloot::{EvalMode, MergeMode, WriteMode, error::DatabaseLockPoisonError};
|
||||
use libloot::{EvalMode, MergeMode, error::DatabaseLockPoisonError};
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
use pyo3::{
|
||||
Bound, PyResult, pyclass, pymethods,
|
||||
@@ -15,6 +15,59 @@ use crate::{
|
||||
metadata::{Group, Message, NONE_REPR, PluginMetadata},
|
||||
};
|
||||
|
||||
#[pyclass(str = "{0:?}")]
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MetadataWriteOptions(libloot::MetadataWriteOptions);
|
||||
|
||||
#[pymethods]
|
||||
impl MetadataWriteOptions {
|
||||
#[new]
|
||||
pub fn new() -> Self {
|
||||
MetadataWriteOptions(libloot::MetadataWriteOptions::new())
|
||||
}
|
||||
|
||||
#[setter]
|
||||
pub fn set_truncate(&mut self, truncate: bool) {
|
||||
self.0.set_truncate(truncate);
|
||||
}
|
||||
|
||||
#[setter]
|
||||
pub fn set_write_anchors(&mut self, write_anchors: bool) {
|
||||
self.0.set_write_anchors(write_anchors);
|
||||
}
|
||||
|
||||
#[setter]
|
||||
pub fn set_write_common_section(&mut self, write_common_section: bool) {
|
||||
self.0.set_write_common_section(write_common_section);
|
||||
}
|
||||
|
||||
#[setter]
|
||||
pub fn set_anchor_file_strings(&mut self, anchor_file_strings: bool) {
|
||||
self.0.set_anchor_file_strings(anchor_file_strings);
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn truncate(&self) -> bool {
|
||||
self.0.truncate()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn write_anchors(&self) -> bool {
|
||||
self.0.write_anchors()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn write_common_section(&self) -> bool {
|
||||
self.0.write_common_section()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn anchor_file_strings(&self) -> bool {
|
||||
self.0.anchor_file_strings()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Database(Arc<RwLock<libloot::Database>>);
|
||||
@@ -56,18 +109,12 @@ impl Database {
|
||||
pub fn write_user_metadata(
|
||||
&self,
|
||||
output_path: PathBuf,
|
||||
overwrite: bool,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_user_metadata(&output_path, write_mode)
|
||||
.write_user_metadata(&output_path, &options.0)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -75,18 +122,12 @@ impl Database {
|
||||
pub fn write_minimal_list(
|
||||
&self,
|
||||
output_path: PathBuf,
|
||||
overwrite: bool,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_minimal_list(&output_path, write_mode)
|
||||
.write_minimal_list(&output_path, &options.0)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -4,7 +4,7 @@ mod game;
|
||||
mod metadata;
|
||||
mod plugin;
|
||||
|
||||
use database::{Database, EdgeType, Vertex};
|
||||
use database::{Database, EdgeType, MetadataWriteOptions, Vertex};
|
||||
use game::{Game, GameType};
|
||||
use metadata::{
|
||||
File, Filename, Group, Location, Message, MessageContent, MessageType, PluginCleaningData,
|
||||
@@ -64,6 +64,7 @@ fn libloot_pyo3(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<TagSuggestion>()?;
|
||||
m.add_class::<Location>()?;
|
||||
m.add_class::<PluginMetadata>()?;
|
||||
m.add_class::<MetadataWriteOptions>()?;
|
||||
|
||||
m.add(
|
||||
"CyclicInteractionError",
|
||||
|
||||
+37
-44
@@ -20,22 +20,6 @@ use crate::{
|
||||
};
|
||||
pub use error::{ConditionEvaluationError, MetadataRetrievalError};
|
||||
|
||||
const WRITE_OPTIONS: MetadataWriteOptions = MetadataWriteOptions {
|
||||
write_anchors: true,
|
||||
write_common_section: true,
|
||||
anchor_file_strings: true,
|
||||
};
|
||||
|
||||
/// Control behaviour when writing to files.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum WriteMode {
|
||||
/// Create the file if it does not exist, otherwise error.
|
||||
Create,
|
||||
/// Create the file if it does not exist, otherwise replace its contents.
|
||||
CreateOrTruncate,
|
||||
}
|
||||
|
||||
/// Control whether user metadata is included or not when retrieving metadata.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[expect(clippy::exhaustive_enums, reason = "It's effectively a boolean")]
|
||||
@@ -116,11 +100,11 @@ impl Database {
|
||||
pub fn write_user_metadata(
|
||||
&self,
|
||||
output_path: &Path,
|
||||
mode: WriteMode,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), WriteMetadataError> {
|
||||
validate_write_path(output_path, mode)?;
|
||||
validate_write_path(output_path, options)?;
|
||||
|
||||
self.userlist.save(output_path, WRITE_OPTIONS)
|
||||
self.userlist.save(output_path, options)
|
||||
}
|
||||
|
||||
/// Writes a metadata file that only contains plugin Bash Tag suggestions
|
||||
@@ -131,9 +115,9 @@ impl Database {
|
||||
pub fn write_minimal_list(
|
||||
&self,
|
||||
output_path: &Path,
|
||||
mode: WriteMode,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), WriteMetadataError> {
|
||||
validate_write_path(output_path, mode)?;
|
||||
validate_write_path(output_path, options)?;
|
||||
|
||||
let mut doc = MetadataDocument::default();
|
||||
|
||||
@@ -145,7 +129,7 @@ impl Database {
|
||||
doc.set_plugin_metadata(minimal_plugin);
|
||||
}
|
||||
|
||||
doc.save(output_path, WRITE_OPTIONS)
|
||||
doc.save(output_path, options)
|
||||
}
|
||||
|
||||
/// Evaluate the given condition string.
|
||||
@@ -351,7 +335,10 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_write_path(output_path: &Path, mode: WriteMode) -> Result<(), WriteMetadataError> {
|
||||
fn validate_write_path(
|
||||
output_path: &Path,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), WriteMetadataError> {
|
||||
if !output_path
|
||||
.parent()
|
||||
.is_some_and(|p| p.as_os_str().is_empty() || p.exists())
|
||||
@@ -360,7 +347,7 @@ fn validate_write_path(output_path: &Path, mode: WriteMode) -> Result<(), WriteM
|
||||
output_path.into(),
|
||||
WriteMetadataErrorReason::ParentDirectoryNotFound,
|
||||
))
|
||||
} else if mode == WriteMode::Create && output_path.exists() {
|
||||
} else if !options.truncate() && output_path.exists() {
|
||||
Err(WriteMetadataError::new(
|
||||
output_path.into(),
|
||||
WriteMetadataErrorReason::PathAlreadyExists,
|
||||
@@ -518,6 +505,12 @@ plugins:
|
||||
}
|
||||
}
|
||||
|
||||
fn truncate_options() -> MetadataWriteOptions {
|
||||
let mut options = MetadataWriteOptions::new();
|
||||
options.set_truncate(true);
|
||||
options
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_masterlist_should_succeed_if_given_a_valid_path() {
|
||||
let fixture = Fixture::new(GameType::Oblivion);
|
||||
@@ -587,7 +580,7 @@ plugins:
|
||||
|
||||
let output_path = fixture.inner.local_path.join("userlist.yaml");
|
||||
database
|
||||
.write_user_metadata(&output_path, WriteMode::Create)
|
||||
.write_user_metadata(&output_path, &MetadataWriteOptions::new())
|
||||
.unwrap();
|
||||
|
||||
let content = std::fs::read_to_string(output_path).unwrap();
|
||||
@@ -603,7 +596,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_user_metadata(&output_path, WriteMode::Create)
|
||||
.write_user_metadata(&output_path, &MetadataWriteOptions::new())
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
@@ -616,7 +609,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_user_metadata(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_user_metadata(&output_path, &truncate_options())
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
@@ -631,7 +624,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_user_metadata(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_user_metadata(&output_path, &truncate_options())
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
@@ -644,7 +637,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_user_metadata(output_path, WriteMode::Create)
|
||||
.write_user_metadata(output_path, &MetadataWriteOptions::new())
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
@@ -657,7 +650,7 @@ plugins:
|
||||
let database = fixture.database();
|
||||
|
||||
let err = database
|
||||
.write_user_metadata(Path::new("/"), WriteMode::Create)
|
||||
.write_user_metadata(Path::new("/"), &MetadataWriteOptions::new())
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
@@ -673,7 +666,7 @@ plugins:
|
||||
let output_path = fixture.inner.local_path;
|
||||
|
||||
let err = database
|
||||
.write_user_metadata(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_user_metadata(&output_path, &truncate_options())
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!("an I/O error occurred", err.to_string());
|
||||
@@ -687,7 +680,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_user_metadata(&output_path, WriteMode::Create)
|
||||
.write_user_metadata(&output_path, &MetadataWriteOptions::new())
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
@@ -706,7 +699,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_user_metadata(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_user_metadata(&output_path, &truncate_options())
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
@@ -721,7 +714,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_user_metadata(&output_path, WriteMode::Create)
|
||||
.write_user_metadata(&output_path, &MetadataWriteOptions::new())
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
@@ -740,7 +733,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(&output_path, WriteMode::Create)
|
||||
.write_minimal_list(&output_path, &MetadataWriteOptions::new())
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
@@ -770,7 +763,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(&output_path, WriteMode::Create)
|
||||
.write_minimal_list(&output_path, &MetadataWriteOptions::new())
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
@@ -783,7 +776,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_minimal_list(&output_path, &truncate_options())
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
@@ -798,7 +791,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_minimal_list(&output_path, &truncate_options())
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
@@ -811,7 +804,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(output_path, WriteMode::Create)
|
||||
.write_minimal_list(output_path, &MetadataWriteOptions::new())
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
@@ -824,7 +817,7 @@ plugins:
|
||||
let database = fixture.database();
|
||||
|
||||
let err = database
|
||||
.write_minimal_list(Path::new("/"), WriteMode::Create)
|
||||
.write_minimal_list(Path::new("/"), &MetadataWriteOptions::new())
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
@@ -840,7 +833,7 @@ plugins:
|
||||
let output_path = fixture.inner.local_path;
|
||||
|
||||
let err = database
|
||||
.write_minimal_list(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_minimal_list(&output_path, &truncate_options())
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!("an I/O error occurred", err.to_string());
|
||||
@@ -854,7 +847,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(&output_path, WriteMode::Create)
|
||||
.write_minimal_list(&output_path, &MetadataWriteOptions::new())
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
@@ -873,7 +866,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(&output_path, WriteMode::CreateOrTruncate)
|
||||
.write_minimal_list(&output_path, &truncate_options())
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
@@ -888,7 +881,7 @@ plugins:
|
||||
|
||||
assert!(
|
||||
database
|
||||
.write_minimal_list(&output_path, WriteMode::Create)
|
||||
.write_minimal_list(&output_path, &MetadataWriteOptions::new())
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
+2
-1
@@ -31,9 +31,10 @@ use std::{path::Path, slice::EscapeAscii};
|
||||
|
||||
use regress::{Error as RegexImplError, Regex};
|
||||
|
||||
pub use database::{Database, EvalMode, MergeMode, WriteMode};
|
||||
pub use database::{Database, EvalMode, MergeMode};
|
||||
pub use game::{Game, GameType};
|
||||
pub use logging::{LogLevel, set_log_level, set_logging_callback};
|
||||
pub use metadata::metadata_document::MetadataWriteOptions;
|
||||
pub use plugin::Plugin;
|
||||
pub use sorting::vertex::{EdgeType, Vertex};
|
||||
pub use version::{
|
||||
|
||||
@@ -28,11 +28,106 @@ use super::{
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub(crate) struct MetadataWriteOptions {
|
||||
pub write_anchors: bool,
|
||||
pub write_common_section: bool,
|
||||
pub anchor_file_strings: bool,
|
||||
/// Options to configure how metadata files are written.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[expect(
|
||||
missing_copy_implementations,
|
||||
reason = "Omitted Copy to allow this to become non-Copy-compatible without breaking backwards compatibility."
|
||||
)]
|
||||
#[expect(
|
||||
clippy::struct_excessive_bools,
|
||||
reason = "It's a config object, the bools make sense."
|
||||
)]
|
||||
pub struct MetadataWriteOptions {
|
||||
truncate: bool,
|
||||
write_anchors: bool,
|
||||
write_common_section: bool,
|
||||
anchor_file_strings: bool,
|
||||
}
|
||||
|
||||
impl MetadataWriteOptions {
|
||||
/// Creates a new set of options, all initially set to `false`.
|
||||
pub fn new() -> Self {
|
||||
MetadataWriteOptions {
|
||||
truncate: false,
|
||||
write_anchors: false,
|
||||
write_common_section: false,
|
||||
anchor_file_strings: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the option to overwrite the output file if it already exists.
|
||||
///
|
||||
/// If true and the file path already exists, its contents will be replaced.
|
||||
///
|
||||
/// If false and the file path already exists, an error will be returned.
|
||||
///
|
||||
/// This setting has no effect if the file path does not exist.
|
||||
pub fn set_truncate(&mut self, truncate: bool) {
|
||||
self.truncate = truncate;
|
||||
}
|
||||
|
||||
/// Sets the option to write YAML anchors and aliases.
|
||||
///
|
||||
/// If true then conditions, constraints, files, file details, plugin
|
||||
/// cleaning data details, messages and message contents that appear more
|
||||
/// than once in the metadata will be deduplicated by including a YAML
|
||||
/// anchor when writing the first occurrence of the value, and writing
|
||||
/// YAML aliases in place of further occurrences.
|
||||
///
|
||||
/// If false, YAML anchors and aliases will not be used, so no deduplication
|
||||
/// will occur.
|
||||
pub fn set_write_anchors(&mut self, write_anchors: bool) {
|
||||
self.write_anchors = write_anchors;
|
||||
}
|
||||
|
||||
/// Sets the option to write YAML anchors in a `common` section.
|
||||
///
|
||||
/// If `write_anchors` is true and this is also true, the document's
|
||||
/// root-level map will start with a `common` key. Its value will be a list
|
||||
/// of all the values for which YAML anchors will be written, so that all
|
||||
/// YAML anchors will appear within that list.
|
||||
///
|
||||
/// This setting has no effect if `write_anchors` is false.
|
||||
pub fn set_write_common_section(&mut self, write_common_section: bool) {
|
||||
self.write_common_section = write_common_section;
|
||||
}
|
||||
|
||||
/// Sets the option to write anchors for [File] values that only have a
|
||||
/// name.
|
||||
///
|
||||
/// If `write_anchors` is true and this is also true, then all repeated
|
||||
/// [File] metadata values will be deduplicated using YAML anchors and
|
||||
/// aliases.
|
||||
///
|
||||
/// If this is false, then only [File] metadata that is serialised
|
||||
/// as a YAML object will be deduplicated (i.e. [File] values that only have
|
||||
/// a name will not be deduplicated).
|
||||
///
|
||||
/// This setting has no effect if `write_anchors` is false.
|
||||
pub fn set_anchor_file_strings(&mut self, anchor_file_strings: bool) {
|
||||
self.anchor_file_strings = anchor_file_strings;
|
||||
}
|
||||
|
||||
/// Gets the current value of the `truncate` option.
|
||||
pub fn truncate(&self) -> bool {
|
||||
self.truncate
|
||||
}
|
||||
|
||||
/// Gets the current value of the `write_anchors` option.
|
||||
pub fn write_anchors(&self) -> bool {
|
||||
self.write_anchors
|
||||
}
|
||||
|
||||
/// Gets the current value of the `write_common_section` option.
|
||||
pub fn write_common_section(&self) -> bool {
|
||||
self.write_common_section
|
||||
}
|
||||
|
||||
/// Gets the current value of the `anchor_file_strings` option.
|
||||
pub fn anchor_file_strings(&self) -> bool {
|
||||
self.anchor_file_strings
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -233,7 +328,7 @@ impl MetadataDocument {
|
||||
pub(crate) fn save(
|
||||
&self,
|
||||
file_path: &Path,
|
||||
options: MetadataWriteOptions,
|
||||
options: &MetadataWriteOptions,
|
||||
) -> Result<(), WriteMetadataError> {
|
||||
logging::trace!("Saving metadata list to: \"{}\"", escape_ascii(file_path));
|
||||
|
||||
@@ -1298,7 +1393,7 @@ plugins:
|
||||
|
||||
let other_path = tmp_dir.path().join("other.yaml");
|
||||
metadata
|
||||
.save(&other_path, MetadataWriteOptions::default())
|
||||
.save(&other_path, &MetadataWriteOptions::default())
|
||||
.unwrap();
|
||||
|
||||
let mut other_metadata = MetadataDocument::default();
|
||||
@@ -1382,16 +1477,11 @@ plugins:
|
||||
|
||||
let metadata = metadata_with_repeated_values();
|
||||
|
||||
metadata
|
||||
.save(
|
||||
&path,
|
||||
MetadataWriteOptions {
|
||||
write_anchors: true,
|
||||
write_common_section: false,
|
||||
anchor_file_strings: true,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let mut options = MetadataWriteOptions::new();
|
||||
options.set_write_anchors(true);
|
||||
options.set_anchor_file_strings(true);
|
||||
|
||||
metadata.save(&path, &options).unwrap();
|
||||
|
||||
let content = std::fs::read_to_string(&path).unwrap();
|
||||
|
||||
@@ -1464,16 +1554,12 @@ plugins:
|
||||
|
||||
let metadata = metadata_with_repeated_values();
|
||||
|
||||
metadata
|
||||
.save(
|
||||
&path,
|
||||
MetadataWriteOptions {
|
||||
write_anchors: true,
|
||||
write_common_section: true,
|
||||
anchor_file_strings: true,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let mut options = MetadataWriteOptions::new();
|
||||
options.set_write_anchors(true);
|
||||
options.set_write_common_section(true);
|
||||
options.set_anchor_file_strings(true);
|
||||
|
||||
metadata.save(&path, &options).unwrap();
|
||||
|
||||
let content = std::fs::read_to_string(&path).unwrap();
|
||||
|
||||
@@ -1555,16 +1641,11 @@ plugins:
|
||||
|
||||
let metadata = metadata_with_repeated_values();
|
||||
|
||||
metadata
|
||||
.save(
|
||||
&path,
|
||||
MetadataWriteOptions {
|
||||
write_anchors: true,
|
||||
write_common_section: true,
|
||||
anchor_file_strings: false,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let mut options = MetadataWriteOptions::new();
|
||||
options.set_write_anchors(true);
|
||||
options.set_write_common_section(true);
|
||||
|
||||
metadata.save(&path, &options).unwrap();
|
||||
|
||||
let content = std::fs::read_to_string(&path).unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user