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:
Oliver Hamlet
2025-04-14 18:31:58 +01:00
parent f69a7ced1d
commit 57057b25ce
13 changed files with 183 additions and 169 deletions
+11 -11
View File
@@ -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,
})
+9 -9
View File
@@ -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(),
})
}
+7 -7
View File
@@ -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
View File
@@ -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) {
+6 -6
View File
@@ -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,
})
}
+55 -50
View File
@@ -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
View File
@@ -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)
}
}
+6
View File
@@ -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::*;
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)];
+4 -4
View File
@@ -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()));
}
}