Skip to content

Commit 2addad3

Browse files
authored
Transform the bin into a lib (#1)
1 parent fca5bf0 commit 2addad3

File tree

4 files changed

+258
-1
lines changed

4 files changed

+258
-1
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Checkout source
1717
uses: actions/checkout@v2
1818

19-
- uses: Swatinem/rust-cargo@v1
19+
- uses: Swatinem/rust-cache@v1
2020

2121
- name: cargo test
2222
uses: actions-rs/cargo@v1

Cargo.lock

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

8+
[dependencies]
9+
thiserror = "1.0.30"
10+
811
[dependencies.windows]
912
version = "0.24.0"
1013
features = [

src/lib.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,199 @@
1+
// Disable warning for the crate name, not a really good way to do this but..
2+
// (https://github.com/rust-lang/rust/issues/45127)
13
#![allow(non_snake_case)]
4+
5+
use std::ffi::{c_void, OsStr, OsString};
6+
use std::mem::size_of;
7+
use std::path::{Path, PathBuf};
8+
use thiserror::Error;
9+
use windows::Win32::Foundation::{CloseHandle, GetLastError, PWSTR, STATUS_PENDING};
10+
use windows::Win32::Security::SECURITY_ATTRIBUTES;
11+
use windows::Win32::System::Threading::{
12+
GetExitCodeProcess, TerminateProcess, WaitForSingleObject, PROCESS_CREATION_FLAGS,
13+
PROCESS_INFORMATION, STARTUPINFOW, WAIT_OBJECT_0,
14+
};
15+
use windows::Win32::System::WindowsProgramming::INFINITE;
16+
17+
#[derive(Error, Debug)]
18+
pub enum Error {
19+
#[error("cannot create process: {0}")]
20+
CreationFailed(u32),
21+
#[error("cannot get exit status: {0}")]
22+
GetExitCodeFailed(u32),
23+
#[error("cannot kill process: {0}")]
24+
KillFailed(u32),
25+
}
26+
27+
type Result<T> = std::result::Result<T, Error>;
28+
29+
#[derive(Debug)]
30+
pub struct Command {
31+
command: OsString,
32+
inherit_handles: bool,
33+
current_directory: Option<PathBuf>,
34+
}
35+
36+
impl Command {
37+
pub fn new(command: impl Into<OsString>) -> Self {
38+
Self {
39+
command: command.into(),
40+
inherit_handles: true,
41+
current_directory: None,
42+
}
43+
}
44+
45+
pub fn inherit_handles(&mut self, inherit: bool) -> &mut Self {
46+
self.inherit_handles = inherit;
47+
self
48+
}
49+
50+
pub fn current_dir(&mut self, dir: impl Into<PathBuf>) -> &mut Self {
51+
self.current_directory = Some(dir.into());
52+
self
53+
}
54+
55+
pub fn spawn(&mut self) -> Result<Child> {
56+
Child::new(
57+
&self.command,
58+
self.inherit_handles,
59+
self.current_directory.as_deref(),
60+
)
61+
}
62+
63+
pub fn status(&mut self) -> Result<ExitStatus> {
64+
self.spawn()?.wait()
65+
}
66+
}
67+
68+
#[derive(Debug)]
69+
pub struct Child {
70+
process_information: PROCESS_INFORMATION,
71+
}
72+
73+
impl Child {
74+
fn new(
75+
command: &OsStr,
76+
inherit_handles: bool,
77+
current_directory: Option<&Path>,
78+
) -> Result<Self> {
79+
let mut startup_info = STARTUPINFOW::default();
80+
let mut process_info = PROCESS_INFORMATION::default();
81+
82+
startup_info.cb = size_of::<STARTUPINFOW>() as u32;
83+
84+
let process_creation_flags = PROCESS_CREATION_FLAGS(0);
85+
86+
let res = unsafe {
87+
if let Some(directory) = current_directory {
88+
let directory = directory.as_os_str();
89+
windows::Win32::System::Threading::CreateProcessW(
90+
PWSTR::default(),
91+
command,
92+
std::ptr::null() as *const SECURITY_ATTRIBUTES,
93+
std::ptr::null() as *const SECURITY_ATTRIBUTES,
94+
inherit_handles,
95+
process_creation_flags,
96+
std::ptr::null() as *const c_void,
97+
directory,
98+
&startup_info,
99+
&mut process_info as *mut PROCESS_INFORMATION,
100+
)
101+
} else {
102+
windows::Win32::System::Threading::CreateProcessW(
103+
PWSTR::default(),
104+
command,
105+
std::ptr::null() as *const SECURITY_ATTRIBUTES,
106+
std::ptr::null() as *const SECURITY_ATTRIBUTES,
107+
inherit_handles,
108+
process_creation_flags,
109+
std::ptr::null() as *const c_void,
110+
PWSTR::default(),
111+
&startup_info,
112+
&mut process_info as *mut PROCESS_INFORMATION,
113+
)
114+
}
115+
};
116+
117+
if res.as_bool() {
118+
Ok(Self {
119+
process_information: process_info,
120+
})
121+
} else {
122+
Err(Error::CreationFailed(unsafe { GetLastError().0 }))
123+
}
124+
}
125+
126+
pub fn wait(&self) -> Result<ExitStatus> {
127+
unsafe {
128+
let mut exit_code: u32 = 0;
129+
let res = WaitForSingleObject(self.process_information.hProcess, INFINITE);
130+
131+
if res == WAIT_OBJECT_0 {
132+
if GetExitCodeProcess(
133+
self.process_information.hProcess,
134+
&mut exit_code as *mut u32,
135+
)
136+
.as_bool()
137+
{
138+
close_handles(&self.process_information);
139+
Ok(ExitStatus(exit_code))
140+
} else {
141+
Err(Error::GetExitCodeFailed(GetLastError().0))
142+
}
143+
} else {
144+
Err(Error::GetExitCodeFailed(GetLastError().0))
145+
}
146+
}
147+
}
148+
149+
pub fn try_wait(&self) -> Result<Option<ExitStatus>> {
150+
unsafe {
151+
let mut exit_code: u32 = 0;
152+
153+
let res = GetExitCodeProcess(
154+
self.process_information.hProcess,
155+
&mut exit_code as *mut u32,
156+
);
157+
158+
if res.as_bool() {
159+
if exit_code == STATUS_PENDING.0 {
160+
Ok(None)
161+
} else {
162+
close_handles(&self.process_information);
163+
Ok(Some(ExitStatus(exit_code)))
164+
}
165+
} else {
166+
Err(Error::GetExitCodeFailed(GetLastError().0))
167+
}
168+
}
169+
}
170+
171+
pub fn kill(&self) -> Result<()> {
172+
unsafe {
173+
let res = TerminateProcess(self.process_information.hProcess, 0);
174+
175+
if res.as_bool() {
176+
Ok(())
177+
} else {
178+
Err(Error::KillFailed(GetLastError().0))
179+
}
180+
}
181+
}
182+
}
183+
184+
pub struct ExitStatus(u32);
185+
186+
impl ExitStatus {
187+
pub fn success(&self) -> bool {
188+
self.0 == 0
189+
}
190+
191+
pub fn code(&self) -> u32 {
192+
self.0
193+
}
194+
}
195+
196+
unsafe fn close_handles(process_info: &PROCESS_INFORMATION) {
197+
CloseHandle(process_info.hProcess);
198+
CloseHandle(process_info.hThread);
199+
}

0 commit comments

Comments
 (0)