Skip to content

Commit

Permalink
feat: add Windows support
Browse files Browse the repository at this point in the history
  • Loading branch information
rfdonnelly committed Aug 1, 2023
1 parent 378bef6 commit 02d45bc
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 27 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ name: ci
jobs:
check:
name: Check
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
- os: macos-11
- os: windows-2019
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ jobs:
- os: ubuntu-20.04
dist-args: --artifacts=local --target=x86_64-unknown-linux-gnu
install-dist: curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.sh | sh
# - os: windows-2019
# dist-args: --artifacts=local --target=x86_64-pc-windows-msvc
# install-dist: irm https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.ps1 | iex
- os: windows-2019
dist-args: --artifacts=local --target=x86_64-pc-windows-msvc
install-dist: irm https://github.com/axodotdev/cargo-dist/releases/download/v0.0.7/cargo-dist-installer.ps1 | iex

runs-on: ${{ matrix.os }}
env:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ installers = []
targets = [
"x86_64-unknown-linux-gnu",
"x86_64-apple-darwin",
# "x86_64-pc-windows-msvc",
"x86_64-pc-windows-msvc",
"aarch64-apple-darwin"
]
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ vsp-router was created to connect two terminal emulators to the same physical RS

* Linux: Yes, tested on Red Hat Enterprise Linux 8
* macOS: Yes, tested on macOS Ventura 13.1
* Windows: No
* Windows: Yes*, tested on Windows 10

*The Windows version does support creation of virtual serial ports. A third-party tool like https://com0com.sourceforge.net[com0com] can be used instead.

## Use Cases

Expand Down
51 changes: 32 additions & 19 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use std::str::FromStr;
pub(crate) struct Cli {
/// Create a virtual serial port.
///
/// NOTE: This option is only applicable on POSIX platforms. This option is
/// not applicable on Windows.
///
/// The argument takes the following form: '[<id>:]<path>'
///
/// If no ID is specified, the ID is set to the basename of the path.
Expand Down Expand Up @@ -113,17 +116,38 @@ pub(crate) struct Route {

impl Cli {
pub(crate) fn validate(&self) -> AppResult<()> {
self.check_windows_virtuals()?;
self.check_duplicate_ids()?;
self.check_route_ids()
}

fn check_windows_virtuals(&self) -> AppResult<()> {
#[cfg(not(unix))]
if !self.virtuals.is_empty() {
Err(anyhow!("the --virtual option is not available on Windows"))
} else {
Ok(())
}

#[cfg(unix)]
Ok(())
}

fn ids(&self) -> impl Iterator<Item = &str> {
#[cfg(unix)]
let virtual_ids = self.virtuals.iter().map(|virtual_| virtual_.id.as_str());
#[cfg(not(unix))]
let virtual_ids = {
let virtual_ids: &[String] = &[];
virtual_ids.iter().map(|virtual_id| virtual_id.as_str())
};
let physical_ids = self.physicals.iter().map(|physical| physical.id.as_str());

virtual_ids.chain(physical_ids)
}

fn check_route_ids(&self) -> AppResult<()> {
let ids = self
.virtuals
.iter()
.map(|virtual_| virtual_.id.as_str())
.chain(self.physicals.iter().map(|physical| physical.id.as_str()))
.collect::<Vec<&str>>();
let ids = self.ids().collect::<Vec<&str>>();

for route in &self.routes {
if !ids.contains(&route.src.as_str()) {
Expand All @@ -149,24 +173,13 @@ impl Cli {

fn check_duplicate_ids(&self) -> AppResult<()> {
let duplicate_ids = self
.virtuals
.iter()
.map(|virtual_| &virtual_.id)
.chain(self.physicals.iter().map(|physical| &physical.id))
.ids()
.fold(HashMap::new(), |mut map, id| {
*map.entry(id).or_insert(0) += 1;
map
})
.iter()
.filter_map(
|(id, &count)| {
if count > 1 {
Some(id.as_str())
} else {
None
}
},
)
.filter_map(|(&id, &count)| if count > 1 { Some(id) } else { None })
.collect::<Vec<&str>>();

if !duplicate_ids.is_empty() {
Expand Down
8 changes: 7 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use camino::{Utf8Path, Utf8PathBuf};
use thiserror::Error;
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
#[cfg(unix)]
use tokio_serial::SerialPort;
use tokio_serial::SerialPortBuilderExt;
use tokio_serial::{SerialPort, SerialStream};
use tokio_serial::SerialStream;
use tokio_stream::{StreamExt, StreamMap};
use tokio_util::io::ReaderStream;
use tracing::{error, info};

use std::collections::HashMap;
use std::fs;

#[cfg(unix)]
use std::os::unix;

#[derive(Error, Debug)]
Expand Down Expand Up @@ -40,6 +44,7 @@ pub struct PtyLink {

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

#[cfg(unix)]
pub fn create_virtual_serial_port<P>(path: P) -> Result<(SerialStream, PtyLink)>
where
P: AsRef<Utf8Path>,
Expand All @@ -59,6 +64,7 @@ where
.map_err(Error::Serial)
}

#[cfg(unix)]
impl PtyLink {
fn new<P: AsRef<Utf8Path>>(subordinate: SerialStream, path: P) -> Result<Self> {
let link = path.as_ref().to_path_buf();
Expand Down
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ mod cli;

use crate::cli::Cli;

use vsp_router::{create_virtual_serial_port, open_physical_serial_port, transfer};
#[cfg(unix)]
use vsp_router::create_virtual_serial_port;

use vsp_router::{open_physical_serial_port, transfer};

use clap::Parser;
use futures_util::future::{AbortHandle, Abortable, Aborted};
Expand All @@ -25,8 +28,10 @@ async fn main() -> AppResult<()> {

let mut sources = StreamMap::new();
let mut sinks = HashMap::new();
#[cfg(unix)]
let mut links = Vec::new();

#[cfg(unix)]
for virtual_ in args.virtuals {
let (port, link) = create_virtual_serial_port(&virtual_.path)?;
let (reader, writer) = tokio::io::split(port);
Expand Down
3 changes: 3 additions & 0 deletions tests/snapshots/cli-help-long.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Options:
--virtual <VIRTUAL>
Create a virtual serial port.

NOTE: This option is only applicable on POSIX platforms. This option is
not applicable on Windows.

The argument takes the following form: '[<id>:]<path>'

If no ID is specified, the ID is set to the basename of the path.
Expand Down

0 comments on commit 02d45bc

Please sign in to comment.