Skip to content
This repository has been archived by the owner on Apr 14, 2024. It is now read-only.

Transform the bin into a lib #1

Merged
merged 38 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ada9525
Transform the bin in a lib
yozhgoor Nov 8, 2021
585cd8e
WIP
yozhgoor Nov 8, 2021
55c90c4
Modify errors messages
yozhgoor Nov 8, 2021
5b9fc15
formatting
yozhgoor Nov 8, 2021
0d0bb47
Merge branch 'main' into lib
yozhgoor Nov 9, 2021
78acca9
Clean up
yozhgoor Nov 9, 2021
e5235c9
Remove main.rs
yozhgoor Nov 9, 2021
493648b
Add arguments to the new function and remove create_process
yozhgoor Nov 9, 2021
0597c31
Attempt to use an Option for the `current_directory` argument
yozhgoor Nov 9, 2021
f0cf958
Clean up
yozhgoor Nov 9, 2021
ec3d73d
Looks good
yozhgoor Nov 9, 2021
844d3a3
Listen to the Clippy
yozhgoor Nov 9, 2021
240f438
Add ExitCode
yozhgoor Nov 9, 2021
6238a18
Remove duplicate
yozhgoor Nov 9, 2021
268c4d1
Better naming
yozhgoor Nov 9, 2021
03522fe
Initialize memory for startup_info
yozhgoor Nov 9, 2021
c933559
Better naming
yozhgoor Nov 9, 2021
dd34f5c
After review
yozhgoor Nov 9, 2021
1c014f4
Create a variable for the process_creation_flags
yozhgoor Nov 9, 2021
2663ea7
Fix error in main workflow
yozhgoor Nov 9, 2021
d94b5b9
Use thiserror for the Error type of the Result
yozhgoor Nov 9, 2021
b689041
Use a path instead of a &str for the current_directory argument
yozhgoor Nov 9, 2021
050f91e
Remove the close_handle function since it's not duplicate code anymore
yozhgoor Nov 9, 2021
e1c520b
Use ExitStatus instead of StatusCode
yozhgoor Nov 9, 2021
9c1e1c7
Remove the command function
yozhgoor Nov 9, 2021
7a8a470
Attempt to add a `try_wait` function
yozhgoor Nov 9, 2021
0004d52
Use a match instead of if else
yozhgoor Nov 9, 2021
bd139ab
Use GetExitCodeProcess in try_wait
yozhgoor Nov 10, 2021
6a36a7a
Modify the wait function to return an exit code
yozhgoor Nov 10, 2021
3da034b
Clean up
yozhgoor Nov 10, 2021
f30270b
Better error handling
yozhgoor Nov 10, 2021
9f8c1cc
Get the error code instead of formatting it into a string
yozhgoor Nov 10, 2021
4b48d31
fmt
yozhgoor Nov 10, 2021
afbfef4
Add a Command struct to create process
yozhgoor Nov 11, 2021
a24a798
After review
yozhgoor Nov 11, 2021
574435f
Oops
yozhgoor Nov 11, 2021
048337d
Add comment about the allowed lint `non_snake_case`
yozhgoor Nov 12, 2021
5eca5fd
After review
yozhgoor Nov 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Checkout source
uses: actions/checkout@v2

- uses: Swatinem/rust-cargo@v1
- uses: Swatinem/rust-cache@v1

- name: cargo test
uses: actions-rs/cargo@v1
Expand Down
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ edition = "2021"

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

[dependencies]
thiserror = "1.0.30"

[dependencies.windows]
version = "0.24.0"
features = [
Expand Down
202 changes: 202 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,203 @@
// Disable warning for the crate name, not a really good way to do this but..
// (https://github.com/rust-lang/rust/issues/45127)
#![allow(non_snake_case)]
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved

use std::ffi::{c_void, OsStr, OsString};
use std::mem::size_of;
use std::path::{Path, PathBuf};
use thiserror::Error;
use windows::Win32::Foundation::{CloseHandle, GetLastError, PWSTR, STATUS_PENDING};
use windows::Win32::Security::SECURITY_ATTRIBUTES;
use windows::Win32::System::Threading::{
GetExitCodeProcess, TerminateProcess, WaitForSingleObject, PROCESS_CREATION_FLAGS,
PROCESS_INFORMATION, STARTUPINFOW, WAIT_OBJECT_0,
};
use windows::Win32::System::WindowsProgramming::INFINITE;

#[derive(Error, Debug)]
pub enum Error {
#[error("cannot create process: {0}")]
CreationFailed(u32),
#[error("cannot get exit status: {0}")]
GetExitCodeFailed(u32),
#[error("cannot kill process: {0}")]
KillFailed(u32),
}

type Result<T> = std::result::Result<T, Error>;

#[derive(Debug)]
pub struct Command {
command: OsString,
inherit_handles: bool,
current_directory: Option<PathBuf>,
}

impl Command {
pub fn new(command: impl Into<OsString>) -> Self {
Self {
command: command.into(),
inherit_handles: true,
current_directory: None,
}
}

pub fn inherit(&mut self, inherit: bool) -> &mut Self {
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved
self.inherit_handles = inherit;
self
}

pub fn current_dir(&mut self, dir: impl Into<PathBuf>) -> &mut Self {
self.current_directory = Some(dir.into());
self
}

pub fn spawn(&mut self) -> Result<Child> {
unsafe {
Child::new(
&self.command,
self.inherit_handles,
self.current_directory.as_deref(),
)
}
}

pub fn status(&mut self) -> Result<ExitStatus> {
match self.spawn() {
Ok(child) => child.wait(),
Err(err) => Err(err),
}
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved
}
}

#[derive(Debug)]
pub struct Child {
process_information: PROCESS_INFORMATION,
}

impl Child {
unsafe fn new(
command: &OsStr,
inherit_handles: bool,
current_directory: Option<&Path>,
) -> Result<Self> {
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved
let mut startup_info = STARTUPINFOW::default();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would start unsafe here in the beginning as the entire logic is one block here

let mut process_info = PROCESS_INFORMATION::default();

startup_info.cb = size_of::<STARTUPINFOW>() as u32;

let process_creation_flags = PROCESS_CREATION_FLAGS(0);

let res = if let Some(directory) = current_directory {
let directory = directory.as_os_str();
windows::Win32::System::Threading::CreateProcessW(
PWSTR::default(),
command,
std::ptr::null() as *const SECURITY_ATTRIBUTES,
std::ptr::null() as *const SECURITY_ATTRIBUTES,
inherit_handles,
process_creation_flags,
std::ptr::null() as *const c_void,
directory,
&startup_info,
&mut process_info as *mut PROCESS_INFORMATION,
)
} else {
windows::Win32::System::Threading::CreateProcessW(
PWSTR::default(),
command,
std::ptr::null() as *const SECURITY_ATTRIBUTES,
std::ptr::null() as *const SECURITY_ATTRIBUTES,
inherit_handles,
process_creation_flags,
std::ptr::null() as *const c_void,
PWSTR::default(),
&startup_info,
&mut process_info as *mut PROCESS_INFORMATION,
)
};

if res.as_bool() {
Ok(Self {
process_information: process_info,
})
} else {
Err(Error::CreationFailed(GetLastError().0))
}
}

pub fn wait(&self) -> Result<ExitStatus> {
let mut exit_code: u32 = 0;

unsafe {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut exit_code: u32 = 0;
unsafe {
unsafe {
let mut exit_code: u32 = 0;

let res = WaitForSingleObject(self.process_information.hProcess, INFINITE);

if res == WAIT_OBJECT_0 {
if GetExitCodeProcess(
self.process_information.hProcess,
&mut exit_code as *mut u32,
)
.as_bool()
{
close_handle(&self.process_information);
Ok(ExitStatus(exit_code))
} else {
Err(Error::GetExitCodeFailed(GetLastError().0))
}
} else {
Err(Error::GetExitCodeFailed(GetLastError().0))
}
}
}

pub fn try_wait(&self) -> Result<Option<ExitStatus>> {
let mut exit_code: u32 = 0;

let res = unsafe {
GetExitCodeProcess(
self.process_information.hProcess,
&mut exit_code as *mut u32,
)
};

if res.as_bool() {
if exit_code == STATUS_PENDING.0 {
Ok(None)
} else {
close_handle(&self.process_information);
Ok(Some(ExitStatus(exit_code)))
}
} else {
Err(Error::GetExitCodeFailed(unsafe { GetLastError().0 }))
}
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn kill(&self) -> Result<()> {
let res = unsafe { TerminateProcess(self.process_information.hProcess, 0) };

if res.as_bool() {
Ok(())
} else {
Err(Error::KillFailed(unsafe { GetLastError().0 }))
}
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved
}
}

pub struct ExitStatus(u32);

impl ExitStatus {
pub fn success(&self) -> bool {
self.0 == 0
}

pub fn code(&self) -> u32 {
self.0
}
}

fn close_handle(process_info: &PROCESS_INFORMATION) {
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved
unsafe {
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
}
}
yozhgoor marked this conversation as resolved.
Show resolved Hide resolved