libsubconverter/utils/
node_manip.rs

1use log::{debug, info};
2use std::cmp::Ordering;
3
4use crate::models::{
5    extra_settings::ExtraSettings,
6    proxy::{Proxy, ProxyType},
7    regex_match_config::RegexMatchConfigs,
8};
9use crate::utils::{
10    matcher::{apply_matcher, reg_find},
11    reg_replace,
12    string::{remove_emoji, trim},
13};
14
15use super::matcher::apply_compiled_rule;
16
17/// Applies a rename configuration to a node
18/// Similar to the C++ nodeRename function
19async fn node_rename(node: &mut Proxy, extra: &mut ExtraSettings) {
20    extra.init_js_context();
21    let rename_array = &extra.rename_array;
22    let original_remark = node.remark.clone();
23    for pattern in rename_array {
24        if !pattern.script.is_empty() {
25            match extra
26                .eval_get_rename_node_remark(node, pattern.script.clone())
27                .await
28            {
29                Ok(new_remark) => {
30                    node.remark = new_remark;
31                }
32                Err(e) => {
33                    log::error!("Error renaming node: {}", e);
34                }
35            }
36        } else if !pattern._match.is_empty() {
37            let mut real_rule = String::new();
38            if apply_matcher(&pattern._match, &mut real_rule, node) && !real_rule.is_empty() {
39                node.remark = reg_replace(&node.remark, &real_rule, &pattern.replace, true, false);
40            }
41        }
42    }
43
44    // If the remark is empty after processing, restore the original
45    if node.remark.is_empty() {
46        node.remark = original_remark;
47    }
48}
49
50/// Adds emoji to node remark based on regex matching
51async fn add_emoji(node: &Proxy, emoji_array: &RegexMatchConfigs, extra: &ExtraSettings) -> String {
52    for pattern in emoji_array {
53        if !pattern.script.is_empty() {
54            match extra
55                .eval_get_emoji_node_remark(node, pattern.script.clone())
56                .await
57            {
58                Ok(emoji) => {
59                    return format!("{} {}", emoji, node.remark);
60                }
61                Err(e) => {
62                    log::error!("Error adding emoji: {}", e);
63                }
64            }
65            continue;
66        }
67
68        // Skip patterns with empty replace
69        if pattern.replace.is_empty() {
70            continue;
71        }
72
73        // Use apply_compiled_rule to handle complex matching rules
74        if let Some(compiled_rule) = &pattern.compiled_rule {
75            if apply_compiled_rule(compiled_rule, node) {
76                return format!("{} {}", pattern.replace, node.remark);
77            }
78        }
79    }
80
81    node.remark.clone()
82}
83
84/// Preprocesses nodes before conversion
85/// Based on the C++ preprocessNodes function
86pub async fn preprocess_nodes(
87    nodes: &mut Vec<Proxy>,
88    extra: &mut ExtraSettings,
89) -> Result<(), Box<dyn std::error::Error>> {
90    // Process each node
91    for node in nodes.iter_mut() {
92        // Remove emoji if needed
93        if extra.remove_emoji {
94            node.remark = trim(&remove_emoji(&node.remark)).to_string();
95        }
96
97        // Apply rename patterns
98        node_rename(node, extra).await;
99
100        // Add emoji if needed
101        if extra.add_emoji {
102            if extra
103                .emoji_array
104                .iter()
105                .any(|pattern| !pattern.script.is_empty())
106            {
107                extra.init_js_context();
108            }
109            node.remark = add_emoji(node, &extra.emoji_array, extra).await;
110        }
111    }
112
113    // Sort nodes if needed
114    if extra.sort_flag && extra.authorized {
115        info!("Sorting {} nodes", nodes.len());
116        extra.eval_sort_nodes(nodes).await?;
117    }
118
119    debug!("Node preprocessing completed for {} nodes", nodes.len());
120    Ok(())
121}
122
123/// Appends proxy type to node remark
124pub fn append_type_to_remark(nodes: &mut Vec<Proxy>) {
125    for node in nodes.iter_mut() {
126        match node.proxy_type {
127            ProxyType::Unknown => {}
128            _ => {
129                let type_str = node.proxy_type.to_string();
130                node.remark = format!("{} ({})", node.remark, type_str);
131            }
132        }
133    }
134}