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}