stew_lib/
lib.rs

1use clap::{App, Arg, ArgMatches};
2
3use crate::config::{
4    Config, FormatEncodingSettings, JPEGEncodingSettings, PNMEncodingSettings, SelectedLicenses,
5};
6use crate::io::{export, import};
7use crate::operations::Operation;
8use crate::processor::encoding_format::EncodingFormatDecider;
9use crate::processor::image_operations::ImageOperationsProcessor;
10use crate::processor::license_display::LicenseDisplayProcessor;
11use crate::processor::{ProcessMutWithConfig, ProcessWithConfig};
12
13mod config;
14mod io;
15pub mod operations;
16mod processor;
17
18pub fn get_app_skeleton(name: &str) -> App<'static, 'static> {
19    App::new(name)
20        .version(env!("CARGO_PKG_VERSION"))
21        .author("Martijn Gribnau <garm@ilumeo.com>")
22        .about("This tool is part of the Stew image toolset. Stew is a set of image transformation tools, adapted from `sic`.")
23        .after_help("For more information, visit: https://github.com/foresterre/stew")
24        .arg(Arg::with_name("forced_output_format")
25            .short("f")
26            .long("output-format")
27            .value_name("FORMAT")
28            .help("Force the output image format to use FORMAT, regardless of the (if any) extension of the given output file path. \
29                Output formats (FORMAT values) supported: BMP, GIF, ICO, JPEG, PNG, PBM, PGM, PPM and PAM.")
30            .takes_value(true))
31        .arg(Arg::with_name("license")
32            .long("license")
33            .help("Displays the license of this piece of software (`stew`).")
34            .takes_value(false))
35        .arg(Arg::with_name("dep_licenses")
36            .long("dep-licenses")
37            .help("Displays the licenses of the dependencies on which this software relies.")
38            .takes_value(false))
39        .arg(Arg::with_name("jpeg_encoding_quality")
40            .long("jpeg-encoding-quality")
41            .help("Set the jpeg quality to QUALITY. Valid values are natural numbers from 1 up to and including 100. Will only be used when the output format is determined to be jpeg.")
42            .value_name("QUALITY")
43            .takes_value(true))
44        .arg(Arg::with_name("pnm_encoding_ascii")
45            .long("pnm-encoding-ascii")
46            .help("Use ascii based encoding when using a PNM image output format (pbm, pgm or ppm). Doesn't apply to 'pam' (PNM Arbitrary Map)."))
47        .arg(Arg::with_name("disable_automatic_color_type_adjustment")
48            .long("disable-automatic-color-type-adjustment")
49            .help("Some image output formats do not support the color type of the image buffer prior to encoding. By default Stew tries to adjust the color type. If this flag is provided, sic will not try to adjust the color type."))
50        .arg(Arg::with_name("input")
51            .long("input")
52            .short("i")
53            .value_name("FILE_INPUT")
54            .takes_value(true)
55            .help("Input of qualified (TBD) image file. Use this file option XOR pipe from stdin."))
56        .arg(Arg::with_name("output")
57            .long("output")
58            .short("o")
59            .value_name("FILE_OUTPUT")
60            .takes_value(true)
61            .help("Output of qualified (TBD) image file. Use this file option XOR pipe to stdout."))
62}
63
64// Here any option should not panic when invalid.
65// Previously, it was allowed to panic within Config, but this is no longer the case.
66pub fn get_default_config(matches: &ArgMatches) -> Result<Config, String> {
67    let res = Config {
68        licenses: match (
69            matches.is_present("license"),
70            matches.is_present("dep_licenses"),
71        ) {
72            (true, true) => vec![
73                SelectedLicenses::ThisSoftware,
74                SelectedLicenses::Dependencies,
75            ],
76            (true, _) => vec![SelectedLicenses::ThisSoftware],
77            (_, true) => vec![SelectedLicenses::Dependencies],
78            _ => vec![],
79        },
80
81        forced_output_format: matches.value_of("forced_output_format").map(String::from),
82
83        disable_automatic_color_type_adjustment: matches
84            .is_present("disable_automatic_color_type_adjustment"),
85
86        encoding_settings: FormatEncodingSettings {
87            // 3 possibilities:
88            //   - present + i (1 ... 100)
89            //   - present + i !(1 ... 100)
90            //   - not present (take default)
91            jpeg_settings: JPEGEncodingSettings::new_result((
92                matches.is_present("jpeg_encoding_quality"),
93                matches.value_of("jpeg_encoding_quality"),
94            ))?,
95            pnm_settings: PNMEncodingSettings::new(matches.is_present("pnm_encoding_ascii")),
96        },
97
98        output: matches.value_of("output").map(|v| v.into()),
99    };
100
101    Ok(res)
102}
103
104/// The run function runs the sic application, taking the matches found by Clap.
105/// This function is separated from the main() function so that it can be used more easily in test cases.
106/// This function consumes the matches provided.
107pub fn run(matches: &ArgMatches, operation: Option<Operation>) -> Result<(), String> {
108    let options = get_default_config(&matches)?;
109
110    if options.output.is_none() {
111        eprintln!(
112            "The default output format is BMP. Use --output-format <FORMAT> to specify \
113             a different output format."
114        );
115    }
116
117    let license_display_processor = LicenseDisplayProcessor::new();
118    license_display_processor.process(&options);
119
120    let mut img = import(matches.value_of("input"))?;
121
122    let mut image_operations_processor = ImageOperationsProcessor::new(&mut img, operation);
123    image_operations_processor.process_mut(&options)?;
124
125    let format_decider = EncodingFormatDecider::new();
126    export(&img, &format_decider, &options)
127}
128
129pub fn run_display_licenses(matches: &ArgMatches) -> Result<(), String> {
130    let options = get_default_config(&matches)?;
131
132    let license_display_processor = LicenseDisplayProcessor::new();
133    let res = license_display_processor.process(&options);
134    Ok(res)
135}