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
19fn node_rename(node: &mut Proxy, rename_array: &RegexMatchConfigs, _extra: &ExtraSettings) {
20    let original_remark = node.remark.clone();
21
22    for pattern in rename_array {
23        // Skip script-based patterns since we're not implementing JavaScript support
24        // here
25        if !pattern._match.is_empty() {
26            let mut real_rule = String::new();
27            if apply_matcher(&pattern._match, &mut real_rule, node) && !real_rule.is_empty() {
28                node.remark = reg_replace(&node.remark, &real_rule, &pattern.replace, true, false);
29            }
30        }
31    }
32
33    // If the remark is empty after processing, restore the original
34    if node.remark.is_empty() {
35        node.remark = original_remark;
36    }
37}
38
39/// Adds emoji to node remark based on regex matching
40fn add_emoji(node: &Proxy, emoji_array: &RegexMatchConfigs, _extra: &ExtraSettings) -> String {
41    for pattern in emoji_array {
42        // Skip patterns with empty replace
43        if pattern.replace.is_empty() {
44            continue;
45        }
46
47        // Use apply_compiled_rule to handle complex matching rules
48        if let Some(compiled_rule) = &pattern.compiled_rule {
49            if apply_compiled_rule(compiled_rule, node) {
50                return format!("{} {}", pattern.replace, node.remark);
51            }
52        }
53    }
54
55    node.remark.clone()
56}
57
58/// Sorts nodes by a specified criterion
59fn sort_nodes(nodes: &mut Vec<Proxy>, _sort_script: &str) {
60    // Skip script-based sorting since we're not implementing JavaScript support
61    // Default sort by remark
62    nodes.sort_by(|a, b| {
63        if a.proxy_type == ProxyType::Unknown {
64            return Ordering::Greater;
65        }
66        if b.proxy_type == ProxyType::Unknown {
67            return Ordering::Less;
68        }
69        a.remark.cmp(&b.remark)
70    });
71}
72
73/// Preprocesses nodes before conversion
74/// Based on the C++ preprocessNodes function
75pub fn preprocess_nodes(
76    nodes: &mut Vec<Proxy>,
77    extra: &ExtraSettings,
78    rename_patterns: &RegexMatchConfigs,
79    emoji_patterns: &RegexMatchConfigs,
80) {
81    // Process each node
82    for node in nodes.iter_mut() {
83        // Remove emoji if needed
84        if extra.remove_emoji {
85            node.remark = trim(&remove_emoji(&node.remark)).to_string();
86        }
87
88        // Apply rename patterns
89        node_rename(node, rename_patterns, extra);
90
91        // Add emoji if needed
92        if extra.add_emoji {
93            node.remark = add_emoji(node, emoji_patterns, extra);
94        }
95    }
96
97    // Sort nodes if needed
98    if extra.sort_flag {
99        info!("Sorting {} nodes", nodes.len());
100        sort_nodes(nodes, &extra.sort_script);
101    }
102
103    debug!("Node preprocessing completed for {} nodes", nodes.len());
104}
105
106/// Appends proxy type to node remark
107pub fn append_type_to_remark(nodes: &mut Vec<Proxy>) {
108    for node in nodes.iter_mut() {
109        match node.proxy_type {
110            ProxyType::Unknown => {}
111            _ => {
112                let type_str = node.proxy_type.to_string();
113                node.remark = format!("{} ({})", node.remark, type_str);
114            }
115        }
116    }
117}