Skip to main content

wire/preference/
body.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! The size of a line of body text a person asked for
7//! (`crates/wire/spec/Preference.lean` D51).
8//!
9//! **The floor is a type, and there is no ceiling.** A number under
10//! [`BODY_PX_MIN`] is refused where it is read — from the person's `[ui]`
11//! file, from a `PutPreferences` frame, and, through the `minimum` the
12//! schema carries, from the answer the page reads — so the three readers
13//! meet one rule instead of three copies of it.
14
15use kernel::{AxCode, AxError};
16use serde::{Deserialize, Serialize};
17
18/// The smallest body size a person may ask for, in pixels.
19///
20/// The floor is the smallest size the person ruled the pages offer; the
21/// colour gate holds its contrast tiers at the stylesheet's default size,
22/// not at this one.
23pub const BODY_PX_MIN: u32 = 12;
24
25/// A body size of at least [`BODY_PX_MIN`] pixels.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
27#[serde(try_from = "u32", into = "u32")]
28pub struct BodyPx(u32);
29
30impl BodyPx {
31    /// Sole constructor.
32    ///
33    /// # Errors
34    /// `E_INVALID_ARGS` for a size under [`BODY_PX_MIN`].
35    pub fn new(px: u32) -> Result<Self, AxError> {
36        if px < BODY_PX_MIN {
37            return Err(
38                AxError::failure(AxCode::InvalidArgs, "set the body size", px.to_string())
39                    .with_recovery(format!(
40                        "give a whole number of pixels of at least {BODY_PX_MIN}"
41                    )),
42            );
43        }
44        Ok(BodyPx(px))
45    }
46
47    #[must_use]
48    pub fn px(self) -> u32 {
49        self.0
50    }
51}
52
53impl TryFrom<u32> for BodyPx {
54    type Error = AxError;
55
56    fn try_from(px: u32) -> Result<Self, AxError> {
57        BodyPx::new(px)
58    }
59}
60
61impl From<BodyPx> for u32 {
62    fn from(size: BodyPx) -> u32 {
63        size.0
64    }
65}
66
67/// Inlined rather than named: a named integer becomes a branded type in
68/// the generated client, and every place the page writes a size would
69/// then have to mint the brand first for no guarantee beyond `minimum`.
70#[cfg(feature = "schema")]
71impl schemars::JsonSchema for BodyPx {
72    fn inline_schema() -> bool {
73        true
74    }
75    fn schema_name() -> std::borrow::Cow<'static, str> {
76        std::borrow::Cow::Borrowed("BodyPx")
77    }
78    fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
79        schemars::json_schema!({
80            "type": "integer",
81            "format": "uint32",
82            "minimum": BODY_PX_MIN,
83        })
84    }
85}