rmap/commands/
view.rs

1use clap::Parser;
2use serde::{Deserialize, Serialize};
3use std::net::Ipv6Addr;
4use std::path::PathBuf;
5
6use crate::commands::Command;
7use crate::commands::common::AddressPredicate;
8use crate::data::{DataRow, DataStreamInfo, DataStreamResult, stream_from_iter};
9
10#[derive(Parser, Serialize, Deserialize)]
11pub struct ViewCommand {
12    /// Path to file containing data to view
13    #[arg(value_name = "FILE")]
14    pub file: PathBuf,
15    /// Column name to select from input data
16    #[arg(short = 'f', long, value_name = "FIELD")]
17    pub field: Option<String>,
18    /// Include addresses matching these predicates (can be specified multiple times)
19    #[arg(long, value_enum)]
20    pub include: Vec<AddressPredicate>,
21    /// Exclude addresses matching these predicates (can be specified multiple times)
22    #[arg(long, value_enum)]
23    pub exclude: Vec<AddressPredicate>,
24    /// Remove duplicate addresses from input dataset before analysis
25    #[arg(short = 'u', long)]
26    pub unique: bool,
27    /// Show the resulting dataframe in an interactive TUI
28    #[arg(long)]
29    pub tui: bool,
30}
31
32impl Command for ViewCommand {
33    async fn run(&self) -> Result<(), String> {
34        // Load and filter addresses from file
35        let addresses = self.load_and_filter_addresses().await?;
36
37        // Convert addresses to DataRow stream
38        let data_rows: Vec<DataRow> = addresses
39            .into_iter()
40            .map(|addr| DataRow::new().with_column("address", addr.to_string()))
41            .collect();
42
43        let headers = vec!["address".to_string()];
44        let info = DataStreamInfo::new(headers)
45            .with_total_rows(data_rows.len())
46            .with_description(format!("Viewing {} addresses from file", data_rows.len()));
47
48        let stream = stream_from_iter(data_rows);
49        Ok(())
50    }
51}
52
53impl ViewCommand {
54    pub fn new(
55        file: PathBuf,
56        field: Option<String>,
57        include: Vec<AddressPredicate>,
58        exclude: Vec<AddressPredicate>,
59        unique: bool,
60        tui: bool,
61    ) -> Self {
62        Self {
63            file,
64            field,
65            include,
66            exclude,
67            unique,
68            tui,
69        }
70    }
71
72    async fn load_and_filter_addresses(&self) -> Result<Vec<Ipv6Addr>, String> {
73        // Read file and parse addresses
74        let content = tokio::fs::read_to_string(&self.file)
75            .await
76            .map_err(|e| format!("Failed to read file: {}", e))?;
77
78        let mut addresses = Vec::new();
79        for line in content.lines() {
80            let line = line.trim();
81            if line.is_empty() {
82                continue;
83            }
84
85            // If a specific field is requested, try to parse as CSV and extract that field
86            let addr_str = if let Some(field) = &self.field {
87                // Simple CSV parsing - could be improved
88                let parts: Vec<&str> = line.split(',').collect();
89                if parts.len() > 0 {
90                    parts[0] // For simplicity, just take first column
91                } else {
92                    line
93                }
94            } else {
95                line
96            };
97
98            if let Ok(addr) = addr_str.parse::<Ipv6Addr>() {
99                addresses.push(addr);
100            }
101        }
102
103        // Apply predicate filters
104        let filtered_addresses = self.apply_predicates(addresses)?;
105
106        // Apply unique filter if requested
107        let final_addresses = if self.unique {
108            let mut unique_set = std::collections::HashSet::new();
109            filtered_addresses
110                .into_iter()
111                .filter(|addr| unique_set.insert(*addr))
112                .collect()
113        } else {
114            filtered_addresses
115        };
116
117        Ok(final_addresses)
118    }
119
120    fn apply_predicates(&self, addresses: Vec<Ipv6Addr>) -> Result<Vec<Ipv6Addr>, String> {
121        let all_predicates = analyze::analysis::predicates::get_all_predicates();
122
123        Ok(addresses
124            .into_iter()
125            .filter(|addr| {
126                // Check include predicates
127                if !self.include.is_empty() {
128                    let include_match = self.include.iter().any(|predicate| {
129                        let filter_name = predicate.to_filter_name();
130                        if let Some((_, predicate_fn)) =
131                            all_predicates.iter().find(|(name, _)| name == &filter_name)
132                        {
133                            predicate_fn(*addr)
134                        } else {
135                            false
136                        }
137                    });
138                    if !include_match {
139                        return false;
140                    }
141                }
142
143                // Check exclude predicates
144                for predicate in &self.exclude {
145                    let filter_name = predicate.to_filter_name();
146                    if let Some((_, predicate_fn)) =
147                        all_predicates.iter().find(|(name, _)| name == &filter_name)
148                    {
149                        if predicate_fn(*addr) {
150                            return false;
151                        }
152                    }
153                }
154
155                true
156            })
157            .collect())
158    }
159}