navcore_signalk_client/units.rs
1//! Fetching a mariner's own display units off the wire.
2//!
3//! The read half only: `signalk-server`'s `unitpreferences` feature has an
4//! admin UI of its own for setting a preference, and nothing in this
5//! workspace has a use for writing one. [`signalk::units`] is what makes
6//! sense of the answer; this module only gets it there.
7//!
8//! Confirmed against a real server: `GET /signalk/v1/unitpreferences/active` answers with
9//! no `Authorization` header at all, on a server with security on and
10//! `allow_readonly: false` -- unlike own-ship data, this is treated as
11//! server-wide configuration rather than the vessel's own information, so
12//! no token is threaded through here.
13
14use std::time::Duration;
15
16use crate::access::agent;
17use crate::{ClientError, Trust};
18
19/// Same figure [`crate::resources::TIMEOUT`] uses, for the same reason:
20/// this is a call on a boat's own LAN.
21const TIMEOUT: Duration = Duration::from_secs(10);
22
23/// Asks the server what it currently prefers, and reads as much of the
24/// answer as [`signalk::units::Preferences::parse`] recognises.
25///
26/// Never answers with an error over a preference merely not stated, or
27/// stated in a unit this crate does not convert -- see
28/// [`signalk::units::Preferences::parse`]'s own doc; those read as an
29/// ordinary, empty [`signalk::units::Preferences`], the state a caller
30/// already has to fall back to its own default unit for regardless.
31///
32/// # Errors
33///
34/// If the server cannot be reached at all, or answers something that is
35/// not JSON -- an older `signalk-server` with no `unitpreferences` feature
36/// answers `404`, which surfaces here rather than being swallowed, since
37/// it is a caller's own decision whether that is worth logging.
38pub fn fetch_active(http_base: &str, trust: &Trust) -> Result<signalk::units::Preferences, ClientError> {
39 let answer: serde_json::Value = agent(trust)?
40 .get(format!("{http_base}/signalk/v1/unitpreferences/active"))
41 .config()
42 .timeout_global(Some(TIMEOUT))
43 .build()
44 .call()
45 .map_err(|error| ClientError::Http(error.to_string()))?
46 .body_mut()
47 .read_json()
48 .map_err(|error| ClientError::Http(error.to_string()))?;
49
50 Ok(signalk::units::Preferences::parse(&answer))
51}