mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Store fixed-size sequences as boxed slices
I.e. Box<[T]> and Box<str> instead of Vec<T> or String.
This commit is contained in:
+11
-11
@@ -18,9 +18,9 @@ use super::{
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct File {
|
||||
name: Filename,
|
||||
display_name: Option<String>,
|
||||
detail: Vec<MessageContent>,
|
||||
condition: Option<String>,
|
||||
display_name: Option<Box<str>>,
|
||||
detail: Box<[MessageContent]>,
|
||||
condition: Option<Box<str>>,
|
||||
}
|
||||
|
||||
impl File {
|
||||
@@ -72,7 +72,7 @@ impl File {
|
||||
/// Set the name to be displayed for the file in messages, formatted using
|
||||
/// CommonMark.
|
||||
pub fn set_display_name(&mut self, display_name: String) -> &mut Self {
|
||||
self.display_name = Some(display_name);
|
||||
self.display_name = Some(display_name.into_boxed_str());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ impl File {
|
||||
detail: Vec<MessageContent>,
|
||||
) -> Result<&mut Self, MultilingualMessageContentsError> {
|
||||
validate_message_contents(&detail)?;
|
||||
self.detail = detail;
|
||||
self.detail = detail.into_boxed_slice();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
@@ -104,20 +104,20 @@ impl File {
|
||||
|
||||
/// Set the condition string.
|
||||
pub fn set_condition(&mut self, condition: String) -> &mut Self {
|
||||
self.condition = Some(condition);
|
||||
self.condition = Some(condition.into_boxed_str());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a case-insensitive filename.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Filename(String);
|
||||
pub struct Filename(Box<str>);
|
||||
|
||||
impl Filename {
|
||||
/// Construct a Filename using the given string.
|
||||
#[must_use]
|
||||
pub fn new(s: String) -> Self {
|
||||
Filename(s)
|
||||
Filename(s.into())
|
||||
}
|
||||
|
||||
/// Get this Filename as a string.
|
||||
@@ -170,7 +170,7 @@ impl TryFromYaml for File {
|
||||
YamlData::String(s) => Ok(File {
|
||||
name: Filename::new(s.clone()),
|
||||
display_name: None,
|
||||
detail: Vec::new(),
|
||||
detail: Box::default(),
|
||||
condition: None,
|
||||
}),
|
||||
YamlData::Hash(h) => {
|
||||
@@ -185,14 +185,14 @@ impl TryFromYaml for File {
|
||||
"detail",
|
||||
YamlObjectType::PluginCleaningData,
|
||||
)?,
|
||||
None => Vec::new(),
|
||||
None => Box::default(),
|
||||
};
|
||||
|
||||
let condition = parse_condition(h, YamlObjectType::File)?;
|
||||
|
||||
Ok(File {
|
||||
name: Filename::new(name.to_string()),
|
||||
display_name: display_name.map(|(_, s)| s.to_string()),
|
||||
display_name: display_name.map(|(_, s)| s.into()),
|
||||
detail,
|
||||
condition,
|
||||
})
|
||||
|
||||
@@ -11,9 +11,9 @@ use super::{
|
||||
/// Represents a group to which plugin metadata objects can belong.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Group {
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
after_groups: Vec<String>,
|
||||
name: Box<str>,
|
||||
description: Option<Box<str>>,
|
||||
after_groups: Box<[String]>,
|
||||
}
|
||||
|
||||
impl Group {
|
||||
@@ -21,7 +21,7 @@ impl Group {
|
||||
#[must_use]
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
name: name.into_boxed_str(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ impl Group {
|
||||
|
||||
/// Set a description for the group.
|
||||
pub fn set_description(&mut self, description: String) -> &mut Self {
|
||||
self.description = Some(description);
|
||||
self.description = Some(description.into_boxed_str());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ impl Group {
|
||||
|
||||
/// Set the names of the groups that this group loads after.
|
||||
pub fn set_after_groups(&mut self, after_groups: Vec<String>) -> &mut Self {
|
||||
self.after_groups = after_groups;
|
||||
self.after_groups = after_groups.into_boxed_slice();
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ impl std::default::Default for Group {
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: Group::DEFAULT_NAME.to_string(),
|
||||
name: Group::DEFAULT_NAME.into(),
|
||||
description: Default::default(),
|
||||
after_groups: Default::default(),
|
||||
}
|
||||
@@ -96,8 +96,8 @@ impl TryFromYaml for Group {
|
||||
let after = get_strings_vec_value(hash, "after", YamlObjectType::Group)?;
|
||||
|
||||
Ok(Group {
|
||||
name: name.to_string(),
|
||||
description: description.map(|d| d.1.to_string()),
|
||||
name: name.into(),
|
||||
description: description.map(|d| d.1.into()),
|
||||
after_groups: after.iter().map(|a| a.to_string()).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ use super::{
|
||||
/// Represents a URL at which the parent plugin can be found.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Location {
|
||||
url: String,
|
||||
name: Option<String>,
|
||||
url: Box<str>,
|
||||
name: Option<Box<str>>,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
@@ -17,7 +17,7 @@ impl Location {
|
||||
#[must_use]
|
||||
pub fn new(url: String) -> Self {
|
||||
Location {
|
||||
url,
|
||||
url: url.into_boxed_str(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ impl Location {
|
||||
|
||||
/// Set a name for the URL, eg. the page or site name.
|
||||
pub fn set_name(&mut self, name: String) -> &mut Self {
|
||||
self.name = Some(name);
|
||||
self.name = Some(name.into_boxed_str());
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ impl TryFromYaml for Location {
|
||||
fn try_from_yaml(value: &MarkedYaml) -> Result<Self, ParseMetadataError> {
|
||||
match &value.data {
|
||||
YamlData::String(s) => Ok(Location {
|
||||
url: s.clone(),
|
||||
url: s.clone().into_boxed_str(),
|
||||
name: None,
|
||||
}),
|
||||
YamlData::Hash(h) => {
|
||||
@@ -68,8 +68,8 @@ impl TryFromYaml for Location {
|
||||
)?;
|
||||
|
||||
Ok(Location {
|
||||
url: link.to_string(),
|
||||
name: Some(name.to_string()),
|
||||
url: link.into(),
|
||||
name: Some(name.into()),
|
||||
})
|
||||
}
|
||||
_ => Err(ParseMetadataError::unexpected_type(
|
||||
|
||||
+21
-25
@@ -43,8 +43,8 @@ impl std::fmt::Display for MessageType {
|
||||
/// Represents a message's localised text content.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct MessageContent {
|
||||
text: String,
|
||||
language: String,
|
||||
text: Box<str>,
|
||||
language: Box<str>,
|
||||
}
|
||||
|
||||
impl MessageContent {
|
||||
@@ -56,7 +56,7 @@ impl MessageContent {
|
||||
#[must_use]
|
||||
pub fn new(text: String) -> Self {
|
||||
MessageContent {
|
||||
text,
|
||||
text: text.into_boxed_str(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ impl MessageContent {
|
||||
|
||||
/// Set the language code to the given value.
|
||||
pub fn set_language(&mut self, language: String) -> &mut Self {
|
||||
self.language = language;
|
||||
self.language = language.into_boxed_str();
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -128,10 +128,10 @@ pub fn select_message_content<'a>(
|
||||
let mut english = None;
|
||||
|
||||
for mc in content {
|
||||
if mc.language == language {
|
||||
if mc.language.as_ref() == language {
|
||||
return Some(mc);
|
||||
} else if matched.is_none() {
|
||||
if language_code.is_some_and(|c| c == mc.language) {
|
||||
if language_code.is_some_and(|c| c == mc.language.as_ref()) {
|
||||
matched = Some(mc);
|
||||
} else if language_code.is_none() {
|
||||
if let Some((content_language_code, _)) = mc.language.split_once('_') {
|
||||
@@ -141,7 +141,7 @@ pub fn select_message_content<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
if mc.language == MessageContent::DEFAULT_LANGUAGE {
|
||||
if mc.language.as_ref() == MessageContent::DEFAULT_LANGUAGE {
|
||||
english = Some(mc);
|
||||
}
|
||||
}
|
||||
@@ -161,8 +161,8 @@ pub fn select_message_content<'a>(
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Message {
|
||||
message_type: MessageType,
|
||||
content: Vec<MessageContent>,
|
||||
condition: Option<String>,
|
||||
content: Box<[MessageContent]>,
|
||||
condition: Option<Box<str>>,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
@@ -172,7 +172,7 @@ impl Message {
|
||||
pub fn new(message_type: MessageType, content: String) -> Self {
|
||||
Self {
|
||||
message_type,
|
||||
content: vec![MessageContent::new(content)],
|
||||
content: Box::new([MessageContent::new(content)]),
|
||||
condition: None,
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ impl Message {
|
||||
|
||||
Ok(Self {
|
||||
message_type,
|
||||
content,
|
||||
content: content.into_boxed_slice(),
|
||||
condition: None,
|
||||
})
|
||||
}
|
||||
@@ -217,7 +217,7 @@ impl Message {
|
||||
|
||||
/// Set the condition string.
|
||||
pub fn set_condition(&mut self, condition: String) -> &mut Self {
|
||||
self.condition = Some(condition);
|
||||
self.condition = Some(condition.into_boxed_str());
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -228,7 +228,7 @@ pub(crate) fn validate_message_contents(
|
||||
if contents.len() > 1 {
|
||||
let english_string_exists = contents
|
||||
.iter()
|
||||
.any(|c| c.language == MessageContent::DEFAULT_LANGUAGE);
|
||||
.any(|c| c.language.as_ref() == MessageContent::DEFAULT_LANGUAGE);
|
||||
|
||||
if !english_string_exists {
|
||||
return Err(MultilingualMessageContentsError {});
|
||||
@@ -249,8 +249,8 @@ impl TryFromYaml for MessageContent {
|
||||
get_required_string_value(value.span.start, hash, "lang", YamlObjectType::Message)?;
|
||||
|
||||
Ok(MessageContent {
|
||||
text: text.to_string(),
|
||||
language: language.to_string(),
|
||||
text: text.into(),
|
||||
language: language.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -259,15 +259,13 @@ pub(crate) fn parse_message_contents_yaml(
|
||||
value: &MarkedYaml,
|
||||
key: &'static str,
|
||||
parent_yaml_type: YamlObjectType,
|
||||
) -> Result<Vec<MessageContent>, ParseMetadataError> {
|
||||
let mut contents = match &value.data {
|
||||
YamlData::String(s) => {
|
||||
vec![MessageContent::new(s.clone())]
|
||||
}
|
||||
) -> Result<Box<[MessageContent]>, ParseMetadataError> {
|
||||
let contents = match &value.data {
|
||||
YamlData::String(s) => Box::new([MessageContent::new(s.clone())]),
|
||||
YamlData::Array(a) => a
|
||||
.iter()
|
||||
.map(MessageContent::try_from_yaml)
|
||||
.collect::<Result<Vec<MessageContent>, _>>()?,
|
||||
.collect::<Result<Box<[_]>, _>>()?,
|
||||
_ => {
|
||||
return Err(ParseMetadataError::unexpected_value_type(
|
||||
value.span.start,
|
||||
@@ -278,8 +276,6 @@ pub(crate) fn parse_message_contents_yaml(
|
||||
}
|
||||
};
|
||||
|
||||
contents.shrink_to_fit();
|
||||
|
||||
if validate_message_contents(&contents).is_err() {
|
||||
Err(ParseMetadataError::new(
|
||||
value.span.start,
|
||||
@@ -342,7 +338,7 @@ impl TryFromYaml for Message {
|
||||
});
|
||||
|
||||
if let Cow::Owned(text) = result {
|
||||
mc.text = text;
|
||||
mc.text = text.into_boxed_str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +352,7 @@ impl TryFromYaml for Message {
|
||||
));
|
||||
}
|
||||
|
||||
mc.text = mc.text.replace(&placeholder, sub);
|
||||
mc.text = mc.text.replace(&placeholder, sub).into_boxed_str();
|
||||
}
|
||||
|
||||
if let Ok(Some(m)) = FMT_REGEX.find(&mc.text) {
|
||||
|
||||
@@ -20,8 +20,8 @@ pub struct PluginCleaningData {
|
||||
itm_count: u32,
|
||||
deleted_reference_count: u32,
|
||||
deleted_navmesh_count: u32,
|
||||
cleaning_utility: String,
|
||||
detail: Vec<MessageContent>,
|
||||
cleaning_utility: Box<str>,
|
||||
detail: Box<[MessageContent]>,
|
||||
}
|
||||
|
||||
impl PluginCleaningData {
|
||||
@@ -32,7 +32,7 @@ impl PluginCleaningData {
|
||||
pub fn new(crc: u32, cleaning_utility: String) -> Self {
|
||||
Self {
|
||||
crc,
|
||||
cleaning_utility,
|
||||
cleaning_utility: cleaning_utility.into_boxed_str(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,7 @@ impl PluginCleaningData {
|
||||
detail: Vec<MessageContent>,
|
||||
) -> Result<&mut Self, MultilingualMessageContentsError> {
|
||||
validate_message_contents(&detail)?;
|
||||
self.detail = detail;
|
||||
self.detail = detail.into_boxed_slice();
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ impl TryFromYaml for PluginCleaningData {
|
||||
Some(n) => {
|
||||
parse_message_contents_yaml(n, "detail", YamlObjectType::PluginCleaningData)?
|
||||
}
|
||||
None => Vec::new(),
|
||||
None => Box::default(),
|
||||
};
|
||||
|
||||
Ok(PluginCleaningData {
|
||||
@@ -174,7 +174,7 @@ impl TryFromYaml for PluginCleaningData {
|
||||
itm_count: itm,
|
||||
deleted_reference_count: udr,
|
||||
deleted_navmesh_count: nav,
|
||||
cleaning_utility: util.to_string(),
|
||||
cleaning_utility: util.into(),
|
||||
detail,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,15 +24,15 @@ pub(crate) const GHOST_FILE_EXTENSION: &str = ".ghost";
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct PluginMetadata {
|
||||
name: PluginName,
|
||||
group: Option<String>,
|
||||
load_after: Vec<File>,
|
||||
requirements: Vec<File>,
|
||||
incompatibilities: Vec<File>,
|
||||
messages: Vec<Message>,
|
||||
tags: Vec<Tag>,
|
||||
dirty_info: Vec<PluginCleaningData>,
|
||||
clean_info: Vec<PluginCleaningData>,
|
||||
locations: Vec<Location>,
|
||||
group: Option<Box<str>>,
|
||||
load_after: Box<[File]>,
|
||||
requirements: Box<[File]>,
|
||||
incompatibilities: Box<[File]>,
|
||||
messages: Box<[Message]>,
|
||||
tags: Box<[Tag]>,
|
||||
dirty_info: Box<[PluginCleaningData]>,
|
||||
clean_info: Box<[PluginCleaningData]>,
|
||||
locations: Box<[Location]>,
|
||||
}
|
||||
|
||||
impl PluginMetadata {
|
||||
@@ -99,7 +99,7 @@ impl PluginMetadata {
|
||||
|
||||
/// Set the plugin's group.
|
||||
pub fn set_group(&mut self, group: String) {
|
||||
self.group = Some(group)
|
||||
self.group = Some(group.into_boxed_str())
|
||||
}
|
||||
|
||||
/// Unsets the plugin's group, so that it is implicitly a member of the
|
||||
@@ -110,42 +110,42 @@ impl PluginMetadata {
|
||||
|
||||
/// Get the plugins that the plugin must load after.
|
||||
pub fn set_load_after_files(&mut self, files: Vec<File>) {
|
||||
self.load_after = files;
|
||||
self.load_after = files.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Get the files that the plugin requires to be installed.
|
||||
pub fn set_requirements(&mut self, files: Vec<File>) {
|
||||
self.requirements = files;
|
||||
self.requirements = files.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Get the files that the plugin is incompatible with.
|
||||
pub fn set_incompatibilities(&mut self, files: Vec<File>) {
|
||||
self.incompatibilities = files;
|
||||
self.incompatibilities = files.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Get the plugin's messages.
|
||||
pub fn set_messages(&mut self, messages: Vec<Message>) {
|
||||
self.messages = messages;
|
||||
self.messages = messages.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Get the plugin's Bash Tag suggestions.
|
||||
pub fn set_tags(&mut self, tags: Vec<Tag>) {
|
||||
self.tags = tags;
|
||||
self.tags = tags.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Get the plugin's dirty plugin information.
|
||||
pub fn set_dirty_info(&mut self, dirty_info: Vec<PluginCleaningData>) {
|
||||
self.dirty_info = dirty_info;
|
||||
self.dirty_info = dirty_info.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Get the plugin's clean plugin information.
|
||||
pub fn set_clean_info(&mut self, clean_info: Vec<PluginCleaningData>) {
|
||||
self.clean_info = clean_info;
|
||||
self.clean_info = clean_info.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Get the locations at which this plugin can be found.
|
||||
pub fn set_locations(&mut self, locations: Vec<Location>) {
|
||||
self.locations = locations;
|
||||
self.locations = locations.into_boxed_slice();
|
||||
}
|
||||
|
||||
/// Merge metadata from the given [PluginMetadata] object into this object.
|
||||
@@ -162,14 +162,21 @@ impl PluginMetadata {
|
||||
self.group = plugin.group.clone();
|
||||
}
|
||||
|
||||
merge_vecs(&mut self.load_after, &plugin.load_after);
|
||||
merge_vecs(&mut self.requirements, &plugin.requirements);
|
||||
merge_vecs(&mut self.incompatibilities, &plugin.incompatibilities);
|
||||
merge_vecs(&mut self.tags, &plugin.tags);
|
||||
self.messages.extend(plugin.messages.iter().cloned());
|
||||
merge_vecs(&mut self.dirty_info, &plugin.dirty_info);
|
||||
merge_vecs(&mut self.clean_info, &plugin.clean_info);
|
||||
merge_vecs(&mut self.locations, &plugin.locations);
|
||||
merge_slices(&mut self.load_after, &plugin.load_after);
|
||||
merge_slices(&mut self.requirements, &plugin.requirements);
|
||||
merge_slices(&mut self.incompatibilities, &plugin.incompatibilities);
|
||||
merge_slices(&mut self.tags, &plugin.tags);
|
||||
|
||||
self.messages = self
|
||||
.messages
|
||||
.iter()
|
||||
.chain(plugin.messages.iter())
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
merge_slices(&mut self.dirty_info, &plugin.dirty_info);
|
||||
merge_slices(&mut self.clean_info, &plugin.clean_info);
|
||||
merge_slices(&mut self.locations, &plugin.locations);
|
||||
}
|
||||
|
||||
/// Check if no plugin metadata is set.
|
||||
@@ -214,13 +221,13 @@ impl PluginMetadata {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct PluginName {
|
||||
string: String,
|
||||
string: Box<str>,
|
||||
regex: Option<Regex>,
|
||||
}
|
||||
|
||||
impl PluginName {
|
||||
fn new(name: &str) -> Result<Self, Box<RegexImplError>> {
|
||||
let name = trim_dot_ghost(name).to_string();
|
||||
let name: Box<str> = trim_dot_ghost(name).into();
|
||||
|
||||
if is_regex_name(&name) {
|
||||
let non_capturing_name = replace_capturing_groups(&name);
|
||||
@@ -245,7 +252,7 @@ impl PluginName {
|
||||
logging::error!("Encountered an error while trying to match the regex {} to the string {}: {}", regex.as_str(), other_name, e);
|
||||
}).unwrap_or(false)
|
||||
} else {
|
||||
unicase::eq(self.string.as_str(), other_name)
|
||||
unicase::eq(self.string.as_ref(), other_name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,13 +314,15 @@ fn is_regex_name(name: &str) -> bool {
|
||||
name.contains(|c| ":\\*?|".chars().any(|n| c == n))
|
||||
}
|
||||
|
||||
fn merge_vecs<T: Clone + PartialEq>(target: &mut Vec<T>, source: &[T]) {
|
||||
let initial_target_len = target.len();
|
||||
fn merge_slices<T: Clone + PartialEq>(target: &mut Box<[T]>, source: &[T]) {
|
||||
let mut vec = target.to_vec();
|
||||
for element in source {
|
||||
if !target[..initial_target_len].contains(element) {
|
||||
target.push(element.clone())
|
||||
if !target.contains(element) {
|
||||
vec.push(element.clone())
|
||||
}
|
||||
}
|
||||
|
||||
*target = vec.into_boxed_slice()
|
||||
}
|
||||
|
||||
fn replace_capturing_groups(regex_string: &str) -> Cow<'_, str> {
|
||||
@@ -375,18 +384,18 @@ impl TryFromYaml for PluginMetadata {
|
||||
|
||||
let group = get_string_value(hash, "group", YamlObjectType::PluginMetadata)?;
|
||||
|
||||
let load_after = get_vec(hash, "after")?;
|
||||
let requirements = get_vec(hash, "req")?;
|
||||
let incompatibilities = get_vec(hash, "inc")?;
|
||||
let messages = get_vec(hash, "msg")?;
|
||||
let tags = get_vec(hash, "tag")?;
|
||||
let dirty_info = get_vec(hash, "dirty")?;
|
||||
let clean_info = get_vec(hash, "clean")?;
|
||||
let locations = get_vec(hash, "url")?;
|
||||
let load_after = get_boxed_slice(hash, "after")?;
|
||||
let requirements = get_boxed_slice(hash, "req")?;
|
||||
let incompatibilities = get_boxed_slice(hash, "inc")?;
|
||||
let messages = get_boxed_slice(hash, "msg")?;
|
||||
let tags = get_boxed_slice(hash, "tag")?;
|
||||
let dirty_info = get_boxed_slice(hash, "dirty")?;
|
||||
let clean_info = get_boxed_slice(hash, "clean")?;
|
||||
let locations = get_boxed_slice(hash, "url")?;
|
||||
|
||||
Ok(PluginMetadata {
|
||||
name,
|
||||
group: group.map(|g| g.1.to_string()),
|
||||
group: group.map(|g| g.1.into()),
|
||||
load_after,
|
||||
requirements,
|
||||
incompatibilities,
|
||||
@@ -399,18 +408,14 @@ impl TryFromYaml for PluginMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_vec<T: TryFromYaml>(
|
||||
fn get_boxed_slice<T: TryFromYaml>(
|
||||
hash: &saphyr::AnnotatedHash<MarkedYaml>,
|
||||
key: &'static str,
|
||||
) -> Result<Vec<T>, ParseMetadataError> {
|
||||
let mut vec = get_as_slice(hash, key, YamlObjectType::PluginMetadata)?
|
||||
) -> Result<Box<[T]>, ParseMetadataError> {
|
||||
get_as_slice(hash, key, YamlObjectType::PluginMetadata)?
|
||||
.iter()
|
||||
.map(|e| T::try_from_yaml(e))
|
||||
.collect::<Result<Vec<T>, _>>()?;
|
||||
|
||||
vec.shrink_to_fit();
|
||||
|
||||
Ok(vec)
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl EmitYaml for PluginMetadata {
|
||||
|
||||
+7
-7
@@ -19,9 +19,9 @@ pub enum TagSuggestion {
|
||||
/// Represents a Bash Tag suggestion for a plugin.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Tag {
|
||||
name: String,
|
||||
name: Box<str>,
|
||||
suggestion: TagSuggestion,
|
||||
condition: Option<String>,
|
||||
condition: Option<Box<str>>,
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
@@ -29,7 +29,7 @@ impl Tag {
|
||||
#[must_use]
|
||||
pub fn new(name: String, suggestion: TagSuggestion) -> Self {
|
||||
Self {
|
||||
name,
|
||||
name: name.into_boxed_str(),
|
||||
suggestion,
|
||||
condition: None,
|
||||
}
|
||||
@@ -59,7 +59,7 @@ impl Tag {
|
||||
|
||||
/// Set the condition string.
|
||||
pub fn set_condition(&mut self, condition: String) -> &mut Self {
|
||||
self.condition = Some(condition);
|
||||
self.condition = Some(condition.into_boxed_str());
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -97,11 +97,11 @@ impl TryFromYaml for Tag {
|
||||
}
|
||||
}
|
||||
|
||||
fn name_and_suggestion(value: &str) -> (String, TagSuggestion) {
|
||||
fn name_and_suggestion(value: &str) -> (Box<str>, TagSuggestion) {
|
||||
if let Some(name) = value.strip_prefix("-") {
|
||||
(name.to_string(), TagSuggestion::Removal)
|
||||
(name.into(), TagSuggestion::Removal)
|
||||
} else {
|
||||
(value.to_string(), TagSuggestion::Addition)
|
||||
(value.into(), TagSuggestion::Addition)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -331,6 +331,12 @@ impl<T: EmitYaml> EmitYaml for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EmitYaml> EmitYaml for Box<[T]> {
|
||||
fn emit_yaml(&self, emitter: &mut YamlEmitter) {
|
||||
self.as_ref().emit_yaml(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -110,7 +110,7 @@ pub fn get_strings_vec_value<'a>(
|
||||
ExpectedType::String,
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>(),
|
||||
.collect(),
|
||||
None => Err(ParseMetadataError::unexpected_value_type(
|
||||
n.span.start,
|
||||
key,
|
||||
@@ -180,14 +180,14 @@ pub fn get_as_slice<'a>(
|
||||
pub fn parse_condition(
|
||||
hash: &saphyr::AnnotatedHash<MarkedYaml>,
|
||||
yaml_type: YamlObjectType,
|
||||
) -> Result<Option<String>, ParseMetadataError> {
|
||||
) -> Result<Option<Box<str>>, ParseMetadataError> {
|
||||
match get_string_value(hash, "condition", yaml_type)? {
|
||||
Some((marker, s)) => {
|
||||
let s = s.to_string();
|
||||
if let Err(e) = Expression::from_str(&s) {
|
||||
return Err(ParseMetadataError::invalid_condition(marker, s, e));
|
||||
}
|
||||
Ok(Some(s))
|
||||
Ok(Some(s.into_boxed_str()))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
|
||||
+7
-6
@@ -48,8 +48,8 @@ pub struct Plugin {
|
||||
game_type: GameType,
|
||||
crc: Option<u32>,
|
||||
version: Option<String>,
|
||||
tags: Vec<String>,
|
||||
archive_paths: Vec<PathBuf>,
|
||||
tags: Box<[String]>,
|
||||
archive_paths: Box<[PathBuf]>,
|
||||
archive_assets: BTreeMap<u64, BTreeSet<u64>>,
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ impl Plugin {
|
||||
};
|
||||
|
||||
let mut version = None;
|
||||
let mut tags = Vec::new();
|
||||
let mut archive_paths = Vec::new();
|
||||
let mut tags = Box::default();
|
||||
let mut archive_paths = Box::default();
|
||||
let mut archive_assets = BTreeMap::new();
|
||||
let plugin =
|
||||
if game_type != GameType::OpenMW || !has_ascii_extension(plugin_path, "omwscripts") {
|
||||
@@ -79,11 +79,12 @@ impl Plugin {
|
||||
plugin.parse_file(parse_options)?;
|
||||
|
||||
if let Some(description) = plugin.description()? {
|
||||
tags = extract_bash_tags(&description);
|
||||
tags = extract_bash_tags(&description).into_boxed_slice();
|
||||
version = extract_version(&description)?;
|
||||
}
|
||||
|
||||
archive_paths = find_associated_archives(game_type, game_cache, plugin_path);
|
||||
archive_paths =
|
||||
find_associated_archives(game_type, game_cache, plugin_path).into_boxed_slice();
|
||||
|
||||
if load_scope == LoadScope::WholePlugin {
|
||||
archive_assets = assets_in_archives(&archive_paths);
|
||||
|
||||
+21
-15
@@ -21,7 +21,7 @@ use super::{
|
||||
error::GroupsPathError,
|
||||
};
|
||||
|
||||
pub type GroupsGraph = Graph<String, EdgeType>;
|
||||
pub type GroupsGraph = Graph<Box<str>, EdgeType>;
|
||||
|
||||
pub fn build_groups_graph(
|
||||
masterlist_groups: &[Group],
|
||||
@@ -49,7 +49,7 @@ pub fn build_groups_graph(
|
||||
EdgeType::UserLoadAfter,
|
||||
)?;
|
||||
|
||||
if let Some(cycle) = find_cycle(&graph, |node| node.clone()) {
|
||||
if let Some(cycle) = find_cycle(&graph, |node| node.clone().into_string()) {
|
||||
Err(CyclicInteractionError::new(cycle).into())
|
||||
} else {
|
||||
Ok(graph)
|
||||
@@ -72,7 +72,7 @@ fn add_groups<'a>(
|
||||
for group in groups {
|
||||
let key = group.name();
|
||||
if !group_nodes.contains_key(key) {
|
||||
let node_index = graph.add_node(group.name().to_string());
|
||||
let node_index = graph.add_node(group.name().into());
|
||||
group_nodes.insert(key, node_index);
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ pub fn find_path(
|
||||
from_group_name: &str,
|
||||
to_group_name: &str,
|
||||
) -> Result<Vec<Vertex>, GroupsPathError> {
|
||||
let float_graph: Graph<&String, f32> = graph.map(
|
||||
let float_graph: Graph<&Box<str>, f32> = graph.map(
|
||||
|_, n| n,
|
||||
|_, e| {
|
||||
if *e == EdgeType::UserLoadAfter {
|
||||
@@ -132,7 +132,7 @@ pub fn find_path(
|
||||
let paths =
|
||||
bellman_ford(&float_graph, from_vertex).map_err(|_| PathfindingError::NegativeCycle)?;
|
||||
|
||||
let mut path = vec![Vertex::new(graph[to_vertex].clone())];
|
||||
let mut path = vec![Vertex::new(graph[to_vertex].clone().into_string())];
|
||||
let mut current = to_vertex;
|
||||
while current != from_vertex {
|
||||
let preceding_vertex = match paths.predecessors.get(current.index()) {
|
||||
@@ -147,7 +147,10 @@ pub fn find_path(
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
_ => {
|
||||
return Err(PathfindingError::PrecedingNodeNotFound(graph[current].clone()).into());
|
||||
return Err(PathfindingError::PrecedingNodeNotFound(
|
||||
graph[current].clone().into_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -164,14 +167,15 @@ pub fn find_path(
|
||||
Some(e) => e,
|
||||
None => {
|
||||
return Err(PathfindingError::EdgeNotFound {
|
||||
from_group: graph[*preceding_vertex].clone(),
|
||||
to_group: graph[current].clone(),
|
||||
from_group: graph[*preceding_vertex].clone().into_string(),
|
||||
to_group: graph[current].clone().into_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let vertex = Vertex::new(graph[*preceding_vertex].clone()).with_out_edge_type(graph[edge]);
|
||||
let vertex = Vertex::new(graph[*preceding_vertex].clone().into_string())
|
||||
.with_out_edge_type(graph[edge]);
|
||||
path.push(vertex);
|
||||
|
||||
current = *preceding_vertex;
|
||||
@@ -183,13 +187,15 @@ pub fn find_path(
|
||||
}
|
||||
|
||||
fn find_node_by_weight(
|
||||
graph: &Graph<String, EdgeType>,
|
||||
graph: &Graph<Box<str>, EdgeType>,
|
||||
weight: &str,
|
||||
) -> Result<NodeIndex, UndefinedGroupError> {
|
||||
match graph
|
||||
.node_indices()
|
||||
.find(|i| graph.node_weight(*i).map(|w| *w == weight).unwrap_or(false))
|
||||
{
|
||||
match graph.node_indices().find(|i| {
|
||||
graph
|
||||
.node_weight(*i)
|
||||
.map(|w| w.as_ref() == weight)
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
Some(n) => Ok(n),
|
||||
None => {
|
||||
logging::error!("Can't find group with name {}", weight);
|
||||
@@ -267,7 +273,7 @@ impl<'a> DfsVisitor<'a> for GroupsPathLengthVisitor {
|
||||
pub fn get_default_group_node(graph: &GroupsGraph) -> Result<NodeIndex, UndefinedGroupError> {
|
||||
graph
|
||||
.node_indices()
|
||||
.find(|n| graph[*n] == Group::DEFAULT_NAME)
|
||||
.find(|n| graph[*n].as_ref() == Group::DEFAULT_NAME)
|
||||
.ok_or_else(|| UndefinedGroupError::new(Group::DEFAULT_NAME.to_string()))
|
||||
}
|
||||
|
||||
|
||||
+26
-26
@@ -32,12 +32,12 @@ pub struct PluginSortingData<'a, T: SortingPlugin> {
|
||||
|
||||
load_order_index: usize,
|
||||
|
||||
pub(super) group: String,
|
||||
pub(super) group: Box<str>,
|
||||
group_is_user_metadata: bool,
|
||||
pub(super) masterlist_load_after: Vec<String>,
|
||||
pub(super) user_load_after: Vec<String>,
|
||||
pub(super) masterlist_req: Vec<String>,
|
||||
pub(super) user_req: Vec<String>,
|
||||
pub(super) masterlist_load_after: Box<[String]>,
|
||||
pub(super) user_load_after: Box<[String]>,
|
||||
pub(super) masterlist_req: Box<[String]>,
|
||||
pub(super) user_req: Box<[String]>,
|
||||
}
|
||||
|
||||
impl<'a, T: SortingPlugin> PluginSortingData<'a, T> {
|
||||
@@ -58,7 +58,7 @@ impl<'a, T: SortingPlugin> PluginSortingData<'a, T> {
|
||||
.and_then(|m| m.group())
|
||||
.or_else(|| masterlist_metadata.and_then(|m| m.group()))
|
||||
.unwrap_or(Group::DEFAULT_NAME)
|
||||
.to_string(),
|
||||
.into(),
|
||||
group_is_user_metadata: user_metadata.and_then(|m| m.group()).is_some(),
|
||||
masterlist_load_after: masterlist_metadata
|
||||
.map(|m| to_filenames(m.load_after_files()))
|
||||
@@ -141,7 +141,7 @@ impl SortingPlugin for Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_filenames(files: &[File]) -> Vec<String> {
|
||||
fn to_filenames(files: &[File]) -> Box<[String]> {
|
||||
files
|
||||
.iter()
|
||||
.map(|f| f.name().as_str().to_string())
|
||||
@@ -987,8 +987,8 @@ struct PathCacher<'a> {
|
||||
|
||||
fn get_plugins_in_groups<T: SortingPlugin>(
|
||||
graph: &InnerPluginsGraph<T>,
|
||||
) -> HashMap<String, Vec<NodeIndex>> {
|
||||
let mut plugins_in_groups: HashMap<String, Vec<NodeIndex>> = HashMap::default();
|
||||
) -> HashMap<Box<str>, Vec<NodeIndex>> {
|
||||
let mut plugins_in_groups: HashMap<Box<str>, Vec<NodeIndex>> = HashMap::default();
|
||||
|
||||
for node in graph.node_indices() {
|
||||
let group_name = graph[node].group.clone();
|
||||
@@ -1047,7 +1047,7 @@ type GroupNodeIndex = NodeIndex;
|
||||
struct GroupsPathVisitor<'a, 'b, 'c, 'd, 'e, T: SortingPlugin> {
|
||||
plugins_graph: &'a mut PluginsGraph<'b, T>,
|
||||
groups_graph: &'e GroupsGraph,
|
||||
groups_plugins: &'c HashMap<String, Vec<PluginNodeIndex>>,
|
||||
groups_plugins: &'c HashMap<Box<str>, Vec<PluginNodeIndex>>,
|
||||
finished_group_vertices: &'d mut HashSet<GroupNodeIndex>,
|
||||
group_node_to_ignore_as_source: Option<GroupNodeIndex>,
|
||||
edge_stack: Vec<(EdgeReference<'e, EdgeType>, &'c [PluginNodeIndex])>,
|
||||
@@ -1058,7 +1058,7 @@ impl<'a, 'b, 'c, 'd, 'e, T: SortingPlugin> GroupsPathVisitor<'a, 'b, 'c, 'd, 'e,
|
||||
fn new(
|
||||
plugins_graph: &'a mut PluginsGraph<'b, T>,
|
||||
groups_graph: &'e GroupsGraph,
|
||||
groups_plugins: &'c HashMap<String, Vec<PluginNodeIndex>>,
|
||||
groups_plugins: &'c HashMap<Box<str>, Vec<PluginNodeIndex>>,
|
||||
finished_group_vertices: &'d mut HashSet<GroupNodeIndex>,
|
||||
group_node_to_ignore_as_source: Option<GroupNodeIndex>,
|
||||
) -> Self {
|
||||
@@ -1080,7 +1080,7 @@ impl<'a, 'b, 'c, 'd, 'e, T: SortingPlugin> GroupsPathVisitor<'a, 'b, 'c, 'd, 'e,
|
||||
|
||||
fn find_plugins_in_group(&self, node_index: GroupNodeIndex) -> &'c [PluginNodeIndex] {
|
||||
self.groups_plugins
|
||||
.get(&self.groups_graph[node_index])
|
||||
.get(self.groups_graph[node_index].as_ref())
|
||||
.map(|v| v.as_slice())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
@@ -3269,7 +3269,7 @@ mod tests {
|
||||
let fixture = Fixture::with_plugins(&[PLUGIN_A, PLUGIN_B]);
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_load_after = vec![PLUGIN_B.into()];
|
||||
a.masterlist_load_after = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3285,7 +3285,7 @@ mod tests {
|
||||
let fixture = Fixture::with_plugins(&[PLUGIN_A, PLUGIN_B]);
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_req = vec![PLUGIN_B.into()];
|
||||
a.masterlist_req = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3381,7 +3381,7 @@ mod tests {
|
||||
fixture.get_plugin_mut(PLUGIN_A).is_master = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_load_after = vec![PLUGIN_B.into()];
|
||||
a.masterlist_load_after = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3407,7 +3407,7 @@ mod tests {
|
||||
fixture.get_plugin_mut(PLUGIN_A).is_master = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.user_load_after = vec![PLUGIN_B.into()];
|
||||
a.user_load_after = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3433,7 +3433,7 @@ mod tests {
|
||||
fixture.get_plugin_mut(PLUGIN_A).is_master = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_req = vec![PLUGIN_B.into()];
|
||||
a.masterlist_req = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3459,7 +3459,7 @@ mod tests {
|
||||
fixture.get_plugin_mut(PLUGIN_A).is_master = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.user_req = vec![PLUGIN_B.into()];
|
||||
a.user_req = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3560,7 +3560,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_load_after = vec![PLUGIN_B.into()];
|
||||
a.masterlist_load_after = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3590,7 +3590,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_load_after = vec![PLUGIN_B.into()];
|
||||
a.masterlist_load_after = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3621,7 +3621,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.user_load_after = vec![PLUGIN_B.into()];
|
||||
a.user_load_after = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3650,7 +3650,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.user_load_after = vec![PLUGIN_B.into()];
|
||||
a.user_load_after = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3681,7 +3681,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_req = vec![PLUGIN_B.into()];
|
||||
a.masterlist_req = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3711,7 +3711,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.masterlist_req = vec![PLUGIN_B.into()];
|
||||
a.masterlist_req = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3742,7 +3742,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.user_req = vec![PLUGIN_B.into()];
|
||||
a.user_req = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
@@ -3771,7 +3771,7 @@ mod tests {
|
||||
b.is_blueprint_plugin = true;
|
||||
|
||||
let mut a = fixture.sorting_data(PLUGIN_A);
|
||||
a.user_req = vec![PLUGIN_B.into()];
|
||||
a.user_req = Box::new([PLUGIN_B.into()]);
|
||||
|
||||
let data = vec![a, fixture.sorting_data(PLUGIN_B)];
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@ pub fn validate_plugin_groups<T: SortingPlugin>(
|
||||
plugins_sorting_data: &[PluginSortingData<'_, T>],
|
||||
groups_graph: &GroupsGraph,
|
||||
) -> Result<(), UndefinedGroupError> {
|
||||
let group_names: HashSet<&String> = groups_graph
|
||||
let group_names: HashSet<&str> = groups_graph
|
||||
.node_indices()
|
||||
.map(|i| &groups_graph[i])
|
||||
.map(|i| groups_graph[i].as_ref())
|
||||
.collect();
|
||||
|
||||
for plugin in plugins_sorting_data {
|
||||
if !group_names.contains(&plugin.group) {
|
||||
return Err(UndefinedGroupError::new(plugin.group.clone()));
|
||||
if !group_names.contains(plugin.group.as_ref()) {
|
||||
return Err(UndefinedGroupError::new(plugin.group.clone().into_string()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user