Skip to main content

astarte_device_sdk/
introspection.rs

1// This file is part of Astarte.
2//
3// Copyright 2024 - 2025 SECO Mind Srl
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//    http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17// SPDX-License-Identifier: Apache-2.0
18
19//! Handle the introspection for the device
20
21use std::{
22    future::Future,
23    path::{Path, PathBuf},
24};
25
26use astarte_interfaces::{Interface, error::Error as InterfaceError};
27use tracing::debug;
28
29use crate::Error;
30
31/// Error while adding an [`Interface`] to the device introspection.
32#[non_exhaustive]
33#[derive(thiserror::Error, Debug)]
34pub enum AddInterfaceError {
35    /// Couldn't add the interface.
36    #[error("error adding interface")]
37    Interface(#[from] InterfaceError),
38    /// Failed to read interface directory.
39    #[error("couldn't read interface path {}", .path.display())]
40    Io {
41        /// The path of the interface json file we couldn't read.
42        path: PathBuf,
43        #[source]
44        /// The IO error.
45        backtrace: std::io::Error,
46    },
47    /// Cannot read the interface file.
48    #[error("invalid interface file {}", .path.display())]
49    InterfaceFile {
50        /// The path of the invalid interface json.
51        path: PathBuf,
52        /// Reason why the interface couldn't be added.
53        #[source]
54        backtrace: InterfaceError,
55    },
56}
57
58impl AddInterfaceError {
59    // Add a path to the error context.
60    pub(crate) fn add_path_context(self, path: PathBuf) -> Self {
61        match self {
62            AddInterfaceError::Interface(backtrace) => {
63                AddInterfaceError::InterfaceFile { path, backtrace }
64            }
65            AddInterfaceError::Io {
66                path: prev,
67                backtrace,
68            } => {
69                debug!("overwriting previous path {}", prev.display());
70
71                AddInterfaceError::Io { path, backtrace }
72            }
73            AddInterfaceError::InterfaceFile {
74                path: prev,
75                backtrace,
76            } => {
77                debug!("overwriting previous path {}", prev.display());
78
79                AddInterfaceError::InterfaceFile { path, backtrace }
80            }
81        }
82    }
83}
84
85/// Trait that permits a client to query the interfaces in the device introspection.
86pub trait DeviceIntrospection {
87    /// Returns a reference to the [`Interface`] with the given name.
88    fn get_interface<F, O>(&self, interface_name: &str, f: F) -> impl Future<Output = O> + Send
89    where
90        F: FnMut(Option<&Interface>) -> O + Send;
91}
92
93/// Trait that permits a client to add and remove interfaces dynamically after being connected.
94pub trait DynamicIntrospection {
95    /// Add a new [`Interface`] to the device introspection.
96    ///
97    /// Returns a bool to check weather the if the interface was added or was already present.
98    fn add_interface(
99        &mut self,
100        interface: Interface,
101    ) -> impl Future<Output = Result<bool, Error>> + Send;
102
103    /// Add one or more [`Interface`] to the device introspection.
104    ///
105    /// Returns a [`Vec`] with the name of the interfaces that have been added.
106    fn extend_interfaces<I>(
107        &mut self,
108        interfaces: I,
109    ) -> impl Future<Output = Result<Vec<String>, Error>> + Send
110    where
111        I: IntoIterator<Item = Interface> + Send;
112
113    /// Add a new interface from the provided file.
114    ///
115    /// Returns a bool to check weather the if the interface was added or was already present.
116    fn add_interface_from_file<P>(
117        &mut self,
118        file_path: P,
119    ) -> impl Future<Output = Result<bool, Error>> + Send
120    where
121        P: AsRef<Path> + Send + Sync;
122
123    /// Add a new interface from a string. The string should contain a valid json formatted
124    /// interface.
125    ///
126    /// Returns a bool to check weather the if the interface was added or was already present.
127    fn add_interface_from_str(
128        &mut self,
129        json_str: &str,
130    ) -> impl Future<Output = Result<bool, Error>> + Send;
131
132    /// Remove the interface with the name specified as argument.
133    ///
134    /// Returns a bool to check weather the if the interface was removed or was missing.
135    fn remove_interface(
136        &mut self,
137        interface_name: &str,
138    ) -> impl Future<Output = Result<bool, Error>> + Send;
139
140    /// Remove interfaces with names specified as argument.
141    ///
142    /// Returns a [`Vec`] with the name of the interfaces that have been removed.
143    fn remove_interfaces<I>(
144        &mut self,
145        interfaces_name: I,
146    ) -> impl Future<Output = Result<Vec<String>, Error>> + Send
147    where
148        I: IntoIterator<Item = String> + Send,
149        I::IntoIter: Send;
150}