Skip to main content

rpfm_extensions/search/
schema.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11/*!
12Module with all the code related to the `SchemaMatches`.
13
14This module contains the code needed to get schema matches from a `GlobalSearch`.
15!*/
16
17use getset::{Getters, MutGetters};
18use serde_derive::{Deserialize, Serialize};
19
20use rpfm_lib::schema::Schema;
21
22use super::{MatchingMode, Searchable};
23
24//-------------------------------------------------------------------------------//
25//                              Enums & Structs
26//-------------------------------------------------------------------------------//
27
28/// This struct represents all the matches of the global search within a Schema.
29#[derive(Debug, Default, Clone, Getters, MutGetters, Serialize, Deserialize)]
30#[getset(get = "pub", get_mut = "pub")]
31pub struct SchemaMatches {
32
33    /// The list of matches within the versioned file.
34    matches: Vec<SchemaMatch>,
35}
36
37/// This struct represents a match on a column name within a Schema.
38#[derive(Debug, Clone, Getters, MutGetters, Serialize, Deserialize)]
39#[getset(get = "pub", get_mut = "pub")]
40pub struct SchemaMatch {
41
42    // The type of versioned file we have.
43    table_name: String,
44
45    // Version of the definition with a match.
46    version: i32,
47
48    // Column of the match.
49    column: u32,
50
51    // Full name of the matched column.
52    column_name: String,
53}
54
55//-------------------------------------------------------------------------------//
56//                             Implementations
57//-------------------------------------------------------------------------------//
58
59impl Searchable for Schema {
60    type SearchMatches = SchemaMatches;
61
62    /// This function performs a search over the provided Text PackedFile.
63    fn search(&self, _file_path: &str, pattern_to_search: &str, case_sensitive: bool, matching_mode: &MatchingMode) -> SchemaMatches {
64        let mut matches = SchemaMatches::default();
65
66        for (table_name, definitions) in self.definitions() {
67            match matching_mode {
68                MatchingMode::Regex(regex) => {
69                    for definition in definitions {
70                        for (index, field) in definition.fields_processed().iter().enumerate() {
71                            if regex.is_match(field.name()) {
72                                matches.matches.push(SchemaMatch::new(
73                                    table_name,
74                                    *definition.version(),
75                                    index as u32,
76                                    field.name()
77                                ));
78                            }
79                        }
80                    }
81                }
82
83                // If we're searching a pattern, we just check every text PackedFile, line by line.
84                MatchingMode::Pattern(regex) => {
85                    let pattern = if case_sensitive || regex.is_some() {
86                        pattern_to_search.to_owned()
87                    } else {
88                        pattern_to_search.to_lowercase()
89                    };
90
91                    for definition in definitions {
92                        for (index, field) in definition.fields_processed().iter().enumerate() {
93                            if case_sensitive {
94                                if field.name().contains(&pattern) {
95                                    matches.matches.push(SchemaMatch::new(
96                                        table_name,
97                                        *definition.version(),
98                                        index as u32,
99                                        field.name()
100                                    ));
101                                }
102                            }
103                            else if let Some(regex) = regex {
104                                if regex.is_match(field.name()) {
105                                    matches.matches.push(SchemaMatch::new(
106                                        table_name,
107                                        *definition.version(),
108                                        index as u32,
109                                        field.name()
110                                    ));
111                                }
112                            }
113
114                            // Fallback, just in case the regex is invalid.
115                            else {
116                                let name = field.name().to_lowercase();
117                                if name.contains(&pattern) {
118                                    matches.matches.push(SchemaMatch::new(
119                                        table_name,
120                                        *definition.version(),
121                                        index as u32,
122                                        field.name()
123                                    ));
124                                }
125                            }
126                        }
127                    }
128                }
129            }
130        }
131
132        matches
133    }
134}
135
136impl SchemaMatch {
137
138    /// This function creates a new `SchemaMatch` with the provided data.
139    pub fn new(table_name: &str, version: i32, column: u32, column_name: &str) -> Self {
140        Self {
141            table_name: table_name.to_owned(),
142            version,
143            column,
144            column_name: column_name.to_owned(),
145        }
146    }
147}