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
//! Relatively generic convenience types.

use std::{
	collections::HashSet,
	error::Error,
	io::ErrorKind,
	sync::Arc,
};
use tokio::sync::Mutex;
use crate::sensor::Sensor;

/// A generic Result type for things that can fail.
pub type ErrResult<T> = Result<T, Box<dyn Error>>;

/// Internal helper for concisely creating an ErrResult.
fn mk_err<E>(kind: ErrorKind, err: E) -> Box<dyn Error>
where E: Into<Box<dyn Error + Send + Sync>>
{
	Box::new(std::io::Error::new(kind, err))
}

/// Construct an `Error` of kind `ErrorKind::NotFound` with the given payload.
pub fn err_not_found<E>(msg: E) -> Box<dyn Error>
where E: Into<Box<dyn Error + Send + Sync>>
{
	mk_err(ErrorKind::NotFound, msg)
}

/// Construct an `Error` of kind `ErrorKind::InvalidData` with the given payload.
pub fn err_invalid_data<E>(msg: E) -> Box<dyn Error>
where E: Into<Box<dyn Error + Send + Sync>>
{
	mk_err(ErrorKind::InvalidData, msg)
}

/// Construct an `Error` of kind `ErrorKind::Unsupported` with the given payload.
pub fn err_unsupported<E>(msg: E) -> Box<dyn Error>
where E: Into<Box<dyn Error + Send + Sync>>
{
	mk_err(ErrorKind::Unsupported, msg)
}

/// Construct an `Error` of kind `ErrorKind::Other` with the given payload.
pub fn err_other<E>(msg: E) -> Box<dyn Error>
where E: Into<Box<dyn Error + Send + Sync>>
{
	mk_err(ErrorKind::Other, msg)
}

/// A data structure to represent an optional allowlist.  This _could_ just be
/// an [`Option`]<[`HashSet`]\<T>> with [`None`] meaning "allow everything", but
/// this is a bit more explicit (and "none means all" is sort of
/// counterintuitive, after all...), and allows more natural, readable querying
/// via [`FilterSet::contains()`].
#[derive(Debug)]
pub enum FilterSet<T> {
	/// A [`FilterSet`] that allows everything.
	All,

	/// A [`FilterSet`] that allows on a specific set of things.
	Only(HashSet<T>),
}

impl<T: Eq + std::hash::Hash> FilterSet<T> {
	/// If `self` is [`Only`](FilterSet::Only), returns `true` if `x` is
	/// contained in the set, and `false` otherwise.  If `self` is
	/// [`All`](FilterSet::All), returns `true` for any `x`.
	pub fn contains(&self, x: &T) -> bool {
		match self {
			Self::All => true,
			Self::Only(set) => set.contains(x),
		}
	}
}

impl<T> From<Option<HashSet<T>>> for FilterSet<T> {
	/// If the [`Option`] is [`Some`] but the set is empty, you get what you
	/// asked for (a filter that rejects everything).
	fn from(set: Option<HashSet<T>>) -> Self {
		match set {
			Some(set) => Self::Only(set),
			None => Self::All,
		}
	}
}

/// Alias for functions for generating dbus `PropertiesChanged` messages.
pub type PropChgMsgFn = dyn Fn(&dbus::Path<'_>, &dyn dbus::arg::RefArg)
                               -> Option<dbus::Message> + Send + Sync;

/// A wrapper type for a [`dbus_crossroads`] dbus interface.  The intent is that
/// `T` is a collection of `PropChgMsgFn`s, one per property of the interface.
pub struct SensorIntf<T> {
	pub token: dbus_crossroads::IfaceToken<Arc<Mutex<Sensor>>>,
	pub msgfns: T,
}

impl<T> SensorIntf<T> {
	/// Build a sensor dbus interface called `intf`.
	///
	/// The properties of the interface are constructed by calling
	/// `mkprops()`, which returns a struct of [`PropChgMsgFn`]s
	/// (e.g. [`ValueIntfMsgFns`](crate::sensor::ValueIntfMsgFns)), which
	/// are returned in combination with the
	/// [`token`](dbus_crossroads::IfaceToken) created for the interface.
	pub fn build<F, I>(cr: &mut dbus_crossroads::Crossroads, intf: I, mkprops: F) -> Self
	where F: FnOnce(&mut dbus_crossroads::IfaceBuilder<Arc<Mutex<Sensor>>>) -> T,
	      I: Into<dbus::strings::Interface<'static>>
	{
		let mut msgfns: Option<T> = None;
		let ctor = |b: &mut dbus_crossroads::IfaceBuilder<Arc<Mutex<Sensor>>>| {
			msgfns = Some(mkprops(b))
		};
		let token = cr.register(intf, ctor);

		Self {
			token,
			msgfns: msgfns.expect("no msgfns set?"),
		}
	}
}

/// A newtype for a dbus path to reduce the likelihood of mixing up inventory
/// paths and sensor paths.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct InventoryPath(pub dbus::Path<'static>);

/// A newtype for a dbus path to reduce the likelihood of mixing up inventory
/// paths and sensor paths.
#[derive(Debug, Clone)]
pub struct SensorPath(pub dbus::Path<'static>);