1 use csv;
2
3 use CliResult;
4 use config::{Config, Delimiter};
5 use select::SelectColumns;
6 use util;
7
8 static USAGE: &'static str = "
9 Select columns from CSV data efficiently.
10
11 This command lets you manipulate the columns in CSV data. You can re-order
12 them, duplicate them or drop them. Columns can be referenced by index or by
13 name if there is a header row (duplicate column names can be disambiguated with
14 more indexing). Finally, column ranges can be specified.
15
16 Select the first and fourth columns:
17 $ xsv select 1,4
18
19 Select the first 4 columns (by index and by name):
20 $ xsv select 1-4
21 $ xsv select Header1-Header4
22
23 Ignore the first 2 columns (by range and by omission):
24 $ xsv select 3-
25 $ xsv select '!1-2'
26
27 Select the third column named 'Foo':
28 $ xsv select 'Foo[2]'
29
30 Re-order and duplicate columns arbitrarily:
31 $ xsv select 3-1,Header3-Header1,Header1,Foo[2],Header1
32
33 Quote column names that conflict with selector syntax:
34 $ xsv select '\"Date - Opening\",\"Date - Actual Closing\"'
35
36 Usage:
37 xsv select [options] [--] <selection> [<input>]
38 xsv select --help
39
40 Common options:
41 -h, --help Display this message
42 -o, --output <file> Write output to <file> instead of stdout.
43 -n, --no-headers When set, the first row will not be interpreted
44 as headers. (i.e., They are not searched, analyzed,
45 sliced, etc.)
46 -d, --delimiter <arg> The field delimiter for reading CSV data.
47 Must be a single character. (default: ,)
48 ";
49
50 #[derive(Deserialize)]
51 struct Args {
52 arg_input: Option<String>,
53 arg_selection: SelectColumns,
54 flag_output: Option<String>,
55 flag_no_headers: bool,
56 flag_delimiter: Option<Delimiter>,
57 }
58
run(argv: &[&str]) -> CliResult<()>59 pub fn run(argv: &[&str]) -> CliResult<()> {
60 let args: Args = util::get_args(USAGE, argv)?;
61
62 let rconfig = Config::new(&args.arg_input)
63 .delimiter(args.flag_delimiter)
64 .no_headers(args.flag_no_headers)
65 .select(args.arg_selection);
66
67 let mut rdr = rconfig.reader()?;
68 let mut wtr = Config::new(&args.flag_output).writer()?;
69
70 let headers = rdr.byte_headers()?.clone();
71 let sel = rconfig.selection(&headers)?;
72
73 if !rconfig.no_headers {
74 wtr.write_record(sel.iter().map(|&i| &headers[i]))?;
75 }
76 let mut record = csv::ByteRecord::new();
77 while rdr.read_byte_record(&mut record)? {
78 wtr.write_record(sel.iter().map(|&i| &record[i]))?;
79 }
80 wtr.flush()?;
81 Ok(())
82 }
83