jerk/
jar.rs

1//! `%JAVA_HOME%\bin\jar` - Create `.jar` files from `.class` files
2//!
3//! | Command       | Description                   | API |
4//! | ------------- | ----------------------------- | --- |
5//! | `jar c...`    | Create `.jar`                 | `jar::Archive{ ... }.create()`
6//! | `jar u...`    | Update `.jar`                 | `jar::Archive{ ... }.update()`
7//! | `jar x...`    | Extract `.jar`                | *NYI*
8//! | `jar t...`    | List `.jar` table of contents | *NYI*
9//! | `jar i...`    | Generate `.jar` index         | *NYI*
10
11use std::io::{Error, ErrorKind};
12use std::path::{Path};
13use std::process::Command;
14
15/// std::io::[Result](https://doc.rust-lang.org/std/io/type.Result.html)
16pub type Result<T> = std::io::Result<T>;
17
18/// Create or update a `.jar` file
19#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct Archive<'a> {
21    pub java_home:      Option<&'a Path>,
22
23    pub jar_file:       Option<&'a Path>,
24    pub manifest_file:  Option<&'a Path>,
25    pub entry_point:    Option<String>,
26
27    pub verbose:                        bool,
28    pub pack200_normalization:          bool,
29    pub uncompressed:                   bool,
30    pub preserve_original_filenames:    bool,
31    pub no_manifest:                    bool,
32
33    pub files: &'a [(&'a Path, &'a [&'a Path])],
34
35    #[doc(hidden)] pub _non_exhaustive: (),
36}
37
38impl<'a> std::default::Default for Archive<'a> {
39    fn default() -> Self {
40        Self {
41            java_home:          None,
42
43            jar_file:           None,
44            manifest_file:      None,
45            entry_point:        None,
46
47            verbose:                        false,
48            pack200_normalization:          false,
49            uncompressed:                   false,
50            preserve_original_filenames:    false,
51            no_manifest:                    false,
52
53            files: &[][..],
54
55            _non_exhaustive:    (),
56        }
57    }
58}
59
60impl<'a> Archive<'a> {
61    pub fn new() -> Self {
62        Default::default()
63    }
64
65    /// Create a new archive
66    ///
67    /// Executes: `jar c...`
68    pub fn create(&self) -> Result<()> {
69        self.exec('c')
70    }
71
72    /// Update an existing archive
73    ///
74    /// Executes: `jar u...`
75    pub fn update(&self) -> Result<()> {
76        self.exec('u')
77    }
78
79    fn exec(&self, create_or_update: char) -> Result<()> {
80        let mut java_home_buf = None;
81        let java_home = self.java_home.or_else(||{
82            java_home_buf = Some(crate::search::find_java_home()?);
83            java_home_buf.as_ref().map(|p| &**p)
84        }).ok_or_else(||
85            Error::new(ErrorKind::NotFound, "JAVA_HOME not set and could not be found, unable to run")
86        )?;
87
88        let mut flags_arg = String::new();
89        for (flag, cond) in [
90            (create_or_update, true),
91            ('v', self.verbose),
92            ('f', self.jar_file.is_some()),
93            ('m', self.manifest_file.is_some()),
94            ('n', self.pack200_normalization),
95            ('e', self.entry_point.is_some()),
96            ('0', self.uncompressed),
97            ('P', self.preserve_original_filenames),
98            ('M', self.no_manifest),
99        ].iter().copied() {
100            if cond {
101                flags_arg.push(flag);
102            }
103        }
104
105        let mut cmd = Command::new(java_home.join("bin/jar"));
106        cmd.arg(&flags_arg);
107
108        if let Some(jar_file)       = self.jar_file.as_ref()        { cmd.arg(jar_file); }
109        if let Some(manifest_file)  = self.manifest_file.as_ref()   { cmd.arg(manifest_file); }
110        if let Some(entry_point)    = self.entry_point.as_ref()     { cmd.arg(entry_point); }
111
112        for (dir, files) in self.files {
113            cmd.arg("-C").arg(dir);
114            for file in *files {
115                cmd.arg(file);
116            }
117        }
118
119        let status = cmd.status()?;
120        if status.success() {
121            Ok(())
122        } else {
123            Err(Error::new(ErrorKind::Other, format!("jar {} ... failed: {:?}", &flags_arg, status)))
124        }
125    }
126}