videostream/
host.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 Au-Zone Technologies
3
4use crate::Error;
5use std::{
6    ffi::{CStr, CString},
7    io,
8    os::unix::prelude::OsStrExt,
9    path::{Path, PathBuf},
10};
11use videostream_sys as ffi;
12
13/// The Host structure provides the frame sharing functionality.  Only a single
14/// host can own frames while a host can have many Client subscribers to the
15/// frames.
16///
17/// A host is created with a socket path which it will own exclusively and
18/// allowing clients to connect in order to receive frames.
19pub struct Host {
20    ptr: *mut ffi::VSLHost,
21}
22
23impl Host {
24    /// Creates a new Host and creates a socket at the specified path on which
25    /// it will listen for client connections.
26    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
27        let path_str_c = CString::new(path.as_ref().as_os_str().as_bytes())?;
28        let ptr = vsl!(vsl_host_init(path_str_c.as_ptr()));
29        if ptr.is_null() {
30            let err = io::Error::last_os_error();
31            return Err(err.into());
32        }
33
34        Ok(Host { ptr })
35    }
36
37    pub fn path(&self) -> Result<PathBuf, Error> {
38        let path_str_c = vsl!(vsl_host_path(self.ptr));
39        if path_str_c.is_null() {
40            return Err(Error::NullPointer);
41        }
42
43        let path_str = unsafe { CStr::from_ptr(path_str_c).to_str()? };
44        Ok(PathBuf::from(path_str))
45    }
46
47    pub fn poll(&self) {}
48
49    pub fn process(&self) {}
50
51    pub fn sockets(&self) {}
52}
53
54impl Drop for Host {
55    fn drop(&mut self) {
56        if let Ok(lib) = ffi::init() {
57            unsafe {
58                lib.vsl_host_release(self.ptr);
59            }
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use std::path::PathBuf;
68
69    #[test]
70    fn test_host() {
71        let path = PathBuf::from("/tmp/test.vsl");
72        let host = Host::new(&path).unwrap();
73        assert_eq!(path, host.path().unwrap());
74        assert!(path.exists());
75        // Rust doesn't provide an is_socket but we at least confirm some things it is
76        // not.
77        assert!(!path.is_file());
78        assert!(!path.is_dir());
79        assert!(!path.is_symlink());
80
81        // FIXME: currently the library will unlink old sockets, this should be
82        // corrected along with adding proper cleanup and error handling when a
83        // socket is already present.
84        //
85        // Creating a second host at the same path should raise an error.
86        // let host2 = Host::new(&path);
87        // assert!(host2.is_err());
88    }
89}