perl_module/resolution/
use_lib.rs1use std::path::Path;
7
8mod extract;
9mod resolve;
10mod statements;
11
12use extract::extract_paths_from_args;
13pub use resolve::resolve_use_lib_paths;
14use statements::{split_perl_statements, strip_no_lib_prefix, strip_use_lib_prefix};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct UseLibPath {
19 pub path: String,
21 pub from_findbin: bool,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum UseLibAction {
28 Add(Vec<UseLibPath>),
30 Remove(Vec<UseLibPath>),
32}
33
34pub fn extract_use_lib_paths(source: &str) -> Vec<UseLibPath> {
47 let mut paths = Vec::new();
48
49 for statement in split_perl_statements(source) {
50 let trimmed = statement.trim();
51 if let Some(rest) = strip_use_lib_prefix(trimmed) {
52 extract_paths_from_args(rest, &mut paths);
53 }
54 }
55
56 paths
57}
58
59#[must_use]
61pub fn extract_use_lib_operations(source: &str) -> Vec<UseLibAction> {
62 let mut ops = Vec::new();
63
64 for statement in split_perl_statements(source) {
65 let trimmed = statement.trim();
66 if let Some(rest) = strip_use_lib_prefix(trimmed) {
67 let mut paths = Vec::new();
68 extract_paths_from_args(rest, &mut paths);
69 if !paths.is_empty() {
70 ops.push(UseLibAction::Add(paths));
71 }
72 continue;
73 }
74
75 if let Some(rest) = strip_no_lib_prefix(trimmed) {
76 let mut paths = Vec::new();
77 extract_paths_from_args(rest, &mut paths);
78 if !paths.is_empty() {
79 ops.push(UseLibAction::Remove(paths));
80 }
81 }
82 }
83
84 ops
85}
86
87#[must_use]
89pub fn resolve_use_lib_paths_from_source(
90 source: &str,
91 workspace_root: &Path,
92 file_dir: Option<&Path>,
93) -> Vec<String> {
94 resolve_use_lib_paths_from_source_at_offset(source, source.len(), workspace_root, file_dir)
95}
96
97#[must_use]
100pub fn resolve_use_lib_paths_from_source_at_offset(
101 source: &str,
102 offset: usize,
103 workspace_root: &Path,
104 file_dir: Option<&Path>,
105) -> Vec<String> {
106 let mut resolved = Vec::new();
107 let source_prefix = source.get(..offset).unwrap_or(source);
108 for op in extract_use_lib_operations(source_prefix) {
109 match op {
110 UseLibAction::Add(paths) => {
111 let added = resolve_use_lib_paths(&paths, workspace_root, file_dir);
112 for path in added.into_iter().rev() {
113 resolved.retain(|existing| existing != &path);
114 resolved.insert(0, path);
115 }
116 }
117 UseLibAction::Remove(paths) => {
118 for path in resolve_use_lib_paths(&paths, workspace_root, file_dir) {
119 resolved.retain(|existing| existing != &path);
120 }
121 }
122 }
123 }
124 resolved
125}
126
127#[must_use]
142pub fn no_lib_cancelled_paths_at_offset(
143 source: &str,
144 offset: usize,
145 workspace_root: &Path,
146 file_dir: Option<&Path>,
147) -> Vec<String> {
148 let mut effective = Vec::<String>::new();
149 let mut cancelled = Vec::<String>::new();
150 let source_prefix = source.get(..offset).unwrap_or(source);
151 for op in extract_use_lib_operations(source_prefix) {
152 match op {
153 UseLibAction::Add(paths) => {
154 let added = resolve_use_lib_paths(&paths, workspace_root, file_dir);
155 for path in &added {
156 cancelled.retain(|c| c != path);
158 }
159 for path in added.into_iter().rev() {
160 effective.retain(|e| e != &path);
161 effective.insert(0, path);
162 }
163 }
164 UseLibAction::Remove(paths) => {
165 let removed = resolve_use_lib_paths(&paths, workspace_root, file_dir);
166 for path in removed {
167 effective.retain(|e| e != &path);
168 if !cancelled.contains(&path) {
169 cancelled.push(path);
170 }
171 }
172 }
173 }
174 }
175 cancelled
176}