1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#![cfg(any(feature = "cq", feature = "tq", feature = "xq", feature = "yq"))]
use super::{exit, stdin_reader, Error};
use serde::Serialize;
use std::env::args;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::process::{Child, Command, Stdio};

pub struct Jq {
    child: Child,
}

impl Jq {
    pub fn new(arguments: &Vec<String>) -> Self {
        let child = match Command::new("jq")
            .args(arguments)
            .stdin(Stdio::piped())
            .spawn()
        {
            Ok(child) => child,
            Err(e) => {
                eprintln!("Error calling jq: {e}");
                exit(Error::JqCalling as i32);
            }
        };
        Self { child }
    }

    pub fn write<T>(&mut self, input: &T)
    where
        T: ?Sized + Serialize,
    {
        let stdin = self.child.stdin.as_mut();
        if stdin.is_none() {
            eprintln!("Error opening jq's STDIN for writing");
            self.wait();
            exit(Error::JqPiping as i32);
        }
        if let Err(e) = &serde_json::to_writer(stdin.unwrap(), input) {
            eprintln!("Error serializing output: {e}");
            self.wait();
            exit(Error::OutputSerialization as i32);
        };
    }

    fn wait(&mut self) {
        if let Err(e) = self.child.wait() {
            eprintln!("Error waiting on jq: {e}");
            exit(Error::JqWaiting as i32);
        }
    }
}

impl Drop for Jq {
    fn drop(&mut self) {
        self.wait();
    }
}

pub fn parse_args() -> (Vec<String>, Vec<String>) {
    let mut arguments: Vec<String> = vec![];
    let mut files: Vec<String> = vec![];
    let mut args_done = false;
    for arg in args().skip(1) {
        if args_done || Path::new(&arg).is_file() {
            files.push(arg);
        } else if arg == "--" {
            args_done = true;
        } else {
            arguments.push(arg);
        }
    }
    (arguments, files)
}

pub fn readers(files: &Vec<String>) -> Vec<Box<dyn BufRead>> {
    if !files.is_empty() {
        let mut file_readers: Vec<Box<dyn BufRead>> = vec![];
        for file_name in files {
            let file = match File::open(file_name) {
                Ok(file) => file,
                Err(e) => {
                    eprintln!("Error opening file {file_name}: {e}");
                    exit(Error::FileOpening as i32);
                }
            };
            file_readers.push(Box::new(BufReader::new(file)))
        }
        file_readers
    } else {
        vec![Box::new(stdin_reader())]
    }
}