use std::path::Path;
mod extract;
mod resolve;
mod statements;
use extract::extract_paths_from_args;
pub use resolve::resolve_use_lib_paths;
use statements::{split_perl_statements, strip_no_lib_prefix, strip_use_lib_prefix};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UseLibPath {
pub path: String,
pub from_findbin: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UseLibAction {
Add(Vec<UseLibPath>),
Remove(Vec<UseLibPath>),
}
pub fn extract_use_lib_paths(source: &str) -> Vec<UseLibPath> {
let mut paths = Vec::new();
for statement in split_perl_statements(source) {
let trimmed = statement.trim();
if let Some(rest) = strip_use_lib_prefix(trimmed) {
extract_paths_from_args(rest, &mut paths);
}
}
paths
}
#[must_use]
pub fn extract_use_lib_operations(source: &str) -> Vec<UseLibAction> {
let mut ops = Vec::new();
for statement in split_perl_statements(source) {
let trimmed = statement.trim();
if let Some(rest) = strip_use_lib_prefix(trimmed) {
let mut paths = Vec::new();
extract_paths_from_args(rest, &mut paths);
if !paths.is_empty() {
ops.push(UseLibAction::Add(paths));
}
continue;
}
if let Some(rest) = strip_no_lib_prefix(trimmed) {
let mut paths = Vec::new();
extract_paths_from_args(rest, &mut paths);
if !paths.is_empty() {
ops.push(UseLibAction::Remove(paths));
}
}
}
ops
}
#[must_use]
pub fn resolve_use_lib_paths_from_source(
source: &str,
workspace_root: &Path,
file_dir: Option<&Path>,
) -> Vec<String> {
resolve_use_lib_paths_from_source_at_offset(source, source.len(), workspace_root, file_dir)
}
#[must_use]
pub fn resolve_use_lib_paths_from_source_at_offset(
source: &str,
offset: usize,
workspace_root: &Path,
file_dir: Option<&Path>,
) -> Vec<String> {
let mut resolved = Vec::new();
let source_prefix = source.get(..offset).unwrap_or(source);
for op in extract_use_lib_operations(source_prefix) {
match op {
UseLibAction::Add(paths) => {
let added = resolve_use_lib_paths(&paths, workspace_root, file_dir);
for path in added.into_iter().rev() {
resolved.retain(|existing| existing != &path);
resolved.insert(0, path);
}
}
UseLibAction::Remove(paths) => {
for path in resolve_use_lib_paths(&paths, workspace_root, file_dir) {
resolved.retain(|existing| existing != &path);
}
}
}
}
resolved
}
#[must_use]
pub fn no_lib_cancelled_paths_at_offset(
source: &str,
offset: usize,
workspace_root: &Path,
file_dir: Option<&Path>,
) -> Vec<String> {
let mut effective = Vec::<String>::new();
let mut cancelled = Vec::<String>::new();
let source_prefix = source.get(..offset).unwrap_or(source);
for op in extract_use_lib_operations(source_prefix) {
match op {
UseLibAction::Add(paths) => {
let added = resolve_use_lib_paths(&paths, workspace_root, file_dir);
for path in &added {
cancelled.retain(|c| c != path);
}
for path in added.into_iter().rev() {
effective.retain(|e| e != &path);
effective.insert(0, path);
}
}
UseLibAction::Remove(paths) => {
let removed = resolve_use_lib_paths(&paths, workspace_root, file_dir);
for path in removed {
effective.retain(|e| e != &path);
if !cancelled.contains(&path) {
cancelled.push(path);
}
}
}
}
}
cancelled
}