1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Mesh resource definitions.
//!
//! Resources are things that can be sent across a mesh node boundary that
//! cannot be serialized to a protobuf field type. This includes mesh ports,
//! Unix file descriptors, and Windows file handles and sockets.

use crate::local_node::Port;
use thiserror::Error;

#[derive(Debug)]
pub enum OsResource {
    #[cfg(unix)]
    Fd(std::os::unix::io::OwnedFd),
    #[cfg(windows)]
    Handle(std::os::windows::io::OwnedHandle),
    #[cfg(windows)]
    Socket(std::os::windows::io::OwnedSocket),
}

/// A resource that can be sent via a port.
#[derive(Debug)]
pub enum Resource {
    /// Another port.
    Port(Port),
    /// An OS resource (file descriptor, Windows handle, or socket).
    Os(OsResource),
}

impl From<Port> for Resource {
    fn from(port: Port) -> Self {
        Self::Port(port)
    }
}

#[derive(Debug, Error)]
pub enum ResourceError {
    #[error("wrong resource type")]
    BadResourceType,
    #[cfg(windows)]
    #[error("failed to convert handle to socket")]
    HandleToSocket(#[source] std::io::Error),
}

impl TryFrom<Resource> for Port {
    type Error = ResourceError;

    fn try_from(value: Resource) -> Result<Self, ResourceError> {
        match value {
            Resource::Port(port) => Ok(port),
            _ => Err(ResourceError::BadResourceType),
        }
    }
}

#[cfg(unix)]
impl From<std::os::unix::io::OwnedFd> for Resource {
    fn from(fd: std::os::unix::io::OwnedFd) -> Self {
        Self::Os(OsResource::Fd(fd))
    }
}

#[cfg(unix)]
impl TryFrom<Resource> for std::os::unix::io::OwnedFd {
    type Error = ResourceError;

    fn try_from(value: Resource) -> Result<Self, ResourceError> {
        match value {
            Resource::Os(OsResource::Fd(fd)) => Ok(fd),
            _ => Err(ResourceError::BadResourceType),
        }
    }
}

#[cfg(windows)]
impl From<std::os::windows::io::OwnedHandle> for Resource {
    fn from(port: std::os::windows::io::OwnedHandle) -> Self {
        Self::Os(OsResource::Handle(port))
    }
}

#[cfg(windows)]
impl TryFrom<Resource> for std::os::windows::io::OwnedHandle {
    type Error = ResourceError;

    fn try_from(value: Resource) -> Result<Self, ResourceError> {
        match value {
            Resource::Os(OsResource::Handle(handle)) => Ok(handle),
            _ => Err(ResourceError::BadResourceType),
        }
    }
}

#[cfg(windows)]
impl From<std::os::windows::io::OwnedSocket> for Resource {
    fn from(port: std::os::windows::io::OwnedSocket) -> Self {
        Self::Os(OsResource::Socket(port))
    }
}

#[cfg(windows)]
impl TryFrom<Resource> for std::os::windows::io::OwnedSocket {
    type Error = ResourceError;

    fn try_from(value: Resource) -> Result<Self, ResourceError> {
        match value {
            Resource::Os(OsResource::Socket(socket)) => Ok(socket),
            Resource::Os(OsResource::Handle(handle)) => {
                pal::windows::OwnedSocketExt::from_handle(handle)
                    .map_err(ResourceError::HandleToSocket)
            }
            _ => Err(ResourceError::BadResourceType),
        }
    }
}

pub type SerializedMessage = mesh_protobuf::SerializedMessage<Resource>;