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