diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b35405b..be41021 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3d13123..837988e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: diff --git a/Cargo.toml b/Cargo.toml index 80e9a8e..a869118 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" ] diff --git a/README.md b/README.md index 0f01d90..d1466fd 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/cli.rs b/src/cli.rs index d871804..1fae830 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -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: '[:]' /// /// If no ID is specified, the ID is set to the basename of the path. @@ -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 { + #[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::>(); + let ids = self.ids().collect::>(); for route in &self.routes { if !ids.contains(&route.src.as_str()) { @@ -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::>(); if !duplicate_ids.is_empty() { diff --git a/src/lib.rs b/src/lib.rs index 1e9655f..782eb65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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)] @@ -40,6 +44,7 @@ pub struct PtyLink { pub type Result = std::result::Result; +#[cfg(unix)] pub fn create_virtual_serial_port

(path: P) -> Result<(SerialStream, PtyLink)> where P: AsRef, @@ -59,6 +64,7 @@ where .map_err(Error::Serial) } +#[cfg(unix)] impl PtyLink { fn new>(subordinate: SerialStream, path: P) -> Result { let link = path.as_ref().to_path_buf(); diff --git a/src/main.rs b/src/main.rs index 0f0c922..320c79f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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}; @@ -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); diff --git a/tests/snapshots/cli-help-long.txt b/tests/snapshots/cli-help-long.txt index 241506d..0497255 100644 --- a/tests/snapshots/cli-help-long.txt +++ b/tests/snapshots/cli-help-long.txt @@ -6,6 +6,9 @@ Options: --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: '[:]' If no ID is specified, the ID is set to the basename of the path.