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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use anyhow::{bail, Result};
use fidl::endpoints::{
    create_endpoints, ClientEnd, DiscoverableProtocolMarker, ServerEnd, ServiceMarker, ServiceProxy,
};
use fidl_fuchsia_io as fio;
use fidl_fuchsia_testing_harness::{RealmProxy_Marker, RealmProxy_Proxy};
use fuchsia_component::client::connect_to_protocol;

pub mod error;
pub use error::Error;

// RealmProxyClient is a client for fuchsia.testing.harness.RealmProxy.
//
// The calling component must have a handle to the RealmProxy protocol in
// order to use this struct. Once the caller has connected to the RealmProxy
// service, they can access the other services in the proxied test realm by
// calling [connect_to_protocol].
//
// # Example Usage
//
// ```
// let realm_proxy = RealmProxyClient::connect()?;
// let echo = realm_proxy.connect_to_protocol::<EchoMarker>().await?;
// ```
pub struct RealmProxyClient {
    inner: RealmProxy_Proxy,
}

impl From<RealmProxy_Proxy> for RealmProxyClient {
    fn from(value: RealmProxy_Proxy) -> Self {
        Self { inner: value }
    }
}

impl From<ClientEnd<RealmProxy_Marker>> for RealmProxyClient {
    fn from(value: ClientEnd<RealmProxy_Marker>) -> Self {
        let inner = value.into_proxy().expect("ClientEnd::into_proxy");
        Self { inner }
    }
}

impl RealmProxyClient {
    // Connects to the RealmProxy service.
    pub fn connect() -> Result<Self, anyhow::Error> {
        let inner = connect_to_protocol::<RealmProxy_Marker>()?;
        Ok(Self { inner })
    }

    // Connects to the protocol marked by [T] via the proxy.
    //
    // Returns an error if the connection fails.
    pub async fn connect_to_protocol<T: DiscoverableProtocolMarker>(
        &self,
    ) -> Result<T::Proxy, anyhow::Error> {
        self.connect_to_named_protocol::<T>(T::PROTOCOL_NAME).await
    }

    // Connects the `sever_end` to the protocol marked by [T] via the proxy.
    //
    // Returns an error if the connection fails.
    pub async fn connect_server_end_to_protocol<T: DiscoverableProtocolMarker>(
        &self,
        server_end: ServerEnd<T>,
    ) -> Result<(), anyhow::Error> {
        self.connect_server_end_to_named_protocol::<T>(T::PROTOCOL_NAME, server_end).await
    }

    // Connects to the protocol with the given name, via the proxy.
    //
    // Returns an error if the connection fails.
    pub async fn connect_to_named_protocol<T: DiscoverableProtocolMarker>(
        &self,
        protocol_name: &str,
    ) -> Result<T::Proxy, anyhow::Error> {
        let (client, server) = create_endpoints::<T>();
        self.connect_server_end_to_named_protocol(protocol_name, server).await?;
        Ok(client.into_proxy()?)
    }

    // Connects the `server_end` to the protocol with the given name, via the proxy.
    //
    // Returns an error if the connection fails.
    pub async fn connect_server_end_to_named_protocol<T: DiscoverableProtocolMarker>(
        &self,
        protocol_name: &str,
        server_end: ServerEnd<T>,
    ) -> Result<(), anyhow::Error> {
        let res =
            self.inner.connect_to_named_protocol(protocol_name, server_end.into_channel()).await?;

        if let Some(op_err) = res.err() {
            bail!("{:?}", op_err);
        }

        Ok(())
    }

    // Opens the given service capability, via the proxy.
    //
    // See https://fuchsia.dev/fuchsia-src/concepts/components/v2/capabilities/service
    // for more information about service capabilities.
    //
    // Returns an error if the connection fails.
    pub async fn open_service<T: ServiceMarker>(
        &self,
    ) -> Result<fio::DirectoryProxy, anyhow::Error> {
        let (client, server) = create_endpoints::<fio::DirectoryMarker>();
        let res = self.inner.open_service(T::SERVICE_NAME, server.into_channel()).await?;
        if let Some(op_err) = res.err() {
            bail!("{:?}", op_err);
        }

        Ok(client.into_proxy()?)
    }

    // Connects to the given service instance, via the proxy.
    //
    // See https://fuchsia.dev/fuchsia-src/concepts/components/v2/capabilities/service
    // for more information about service capabilities.
    //
    // Returns an error if the connection fails.
    pub async fn connect_to_service_instance<T: ServiceMarker>(
        &self,
        instance: &str,
    ) -> Result<T::Proxy, anyhow::Error> {
        let (client, server) = create_endpoints::<fio::DirectoryMarker>();
        let res = self
            .inner
            .connect_to_service_instance(T::SERVICE_NAME, instance, server.into_channel())
            .await?;
        if let Some(op_err) = res.err() {
            bail!("{:?}", op_err);
        }

        Ok(T::Proxy::from_member_opener(Box::new(
            fuchsia_component::client::ServiceInstanceDirectory(client.into_proxy()?),
        )))
    }
}