rat 0.4.12

REST API tool - query various REST APIs comfortably
Documentation
use super::client;

use config::{Config, OutputFormat};
use utils::console::*;
use utils::output;

use clap::{App, Arg, ArgMatches, SubCommand};
use serde_json;
use std::fmt;
use std::str;

pub const NAME: &'static str = "search";

error_chain! {
    errors {
        CenterDeviceSearchFailed {
            description("failed to search for documents")
            display("failed to search for documents")
        }

        OutputFailed {
            description("output failed")
            display("output failed")
        }
    }
}

pub fn build_sub_cli() -> App<'static, 'static> {
    SubCommand::with_name(NAME)
        .about("Search for documents in CenterDevice")
        .arg(Arg::with_name("filename")
            .long("filename")
            .short("f")
            .takes_value(true)
            .multiple(true)
            .help("Add filename to search"))
        .arg(Arg::with_name("tags")
            .long("tag")
            .short("t")
            .takes_value(true)
            .multiple(true)
            .help("Add tag to search"))
        .arg(Arg::with_name("public_collections")
            .long("public-collections")
            .short("p")
            .help("Includes public collections in search"))
        .arg(Arg::with_name("fulltext")
            .index(1)
            .help("Add fulltext to search"))
}

pub fn call(args: Option<&ArgMatches>, config: &Config) -> Result<()> {
    let args = args.unwrap();

    let filenames: Option<Vec<&str>> = args.values_of("filename").map(|c| c.collect());
    let tags: Option<Vec<&str>> = args.values_of("tags").map(|c| c.collect());
    let named = if args.is_present("public_collections") {
        client::search::NamedSearches::PublicCollections
    } else {
        client::search::NamedSearches::None
    };
    let fulltext = args.value_of("fulltext");

    info("Searching for documents ...");
    if named == client::search::NamedSearches::PublicCollections {
        info("Including public collections");
    }
    let json = client::search_documents(
        &config.centerdevice.api_base_url,
        config.centerdevice.access_token.as_ref().unwrap(), filenames, tags, fulltext, named)
        .chain_err(|| ErrorKind::CenterDeviceSearchFailed)?;

    output(&json, &config.general.output_format)
}


fn output(json: &str, format: &OutputFormat) -> Result<()> {
    match *format {
        OutputFormat::HUMAN => output_human(json),
        OutputFormat::JSON => output::as_json(json).chain_err(|| ErrorKind::OutputFailed),
    }
}

#[derive(Deserialize, Debug)]
struct SearchResult {
    hits: u64,
    documents: Option<Vec<Documents>>,
}

#[derive(Deserialize, Debug)]
struct Documents {
    id: String,
    version: u32,
    filename: String,
    size: u64,
    #[serde(rename(deserialize = "upload-date"))] upload_date: String,
    #[serde(rename(deserialize = "version-date"))] version_date: String,
    representations: Representations,
}

#[derive(Deserialize, Debug)]
struct Representations {
    pdf: String,
    fulltext: String,
    jpg: String,
    png: String,
    mp4: String,
}

impl fmt::Display for Representations {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut reps = Vec::new();
        if self.pdf == "yes" { reps.push("pdf"); }
        if self.fulltext == "yes" { reps.push("fulltext"); }
        if self.jpg == "yes" { reps.push("jpg"); }
        if self.png == "yes" { reps.push("png"); }
        if self.mp4 == "yes" { reps.push("mp4"); }

        write!(f, "{:?}", reps)
    }
}

fn output_human(json: &str) -> Result<()> {
    let result: SearchResult = serde_json::from_str(json).chain_err(|| "JSON parsing failed")?;
    msgln(format!("Found {} document(s) matching the search parameters:", result.hits));

    if let Some(documents) = result.documents {
        for d in documents {
            msgln(format!("* {}: '{}', version {}, {} bytes, uploaded {}, created {}, {}",
                          d.id, d.filename, d.version, d.size, d.version_date, d.upload_date, d.representations));
        }
    }

    Ok(())
}