| 382 |
382 |
|
//! that one page: audiofiles' library browser and goingson's filters are the
|
| 383 |
383 |
|
//! same shape.
|
| 384 |
384 |
|
//!
|
|
385 |
+ |
//! 0.39.0 gives an *option* its second line. [`Choice::detail`] is the sentence
|
|
386 |
+ |
//! that says what picking one means, and it is one member and not two. Measured
|
|
387 |
+ |
//! 2026-08-29 (`5e21dcfc`), off the MNW server's un-ported markup.
|
|
388 |
+ |
//!
|
|
389 |
+ |
//! - **Six sites, and four of them had already folded it into the label.**
|
|
390 |
+ |
//! `<strong>Public</strong>: Anyone can see this repository` in git settings,
|
|
391 |
+ |
//! the same shape in the project-basics AI tier and the cart's currency
|
|
392 |
+ |
//! conversion, `Mislabeled (wrong AI tier or category)` in the report modal.
|
|
393 |
+ |
//! The item-type and item-pricing wizards give it its own span. The described
|
|
394 |
+ |
//! screens fold it in miniature: `Every 15 minutes (recommended)`. One fact,
|
|
395 |
+ |
//! six spellings, and the folding is what says the member was missing.
|
|
396 |
+ |
//! - **It is not a price**, and the count is what decided that rather than
|
|
397 |
+ |
//! taste. The site that asked carries a name, a price and a description; the
|
|
398 |
+ |
//! tree's other three priced tier lists are not option lists at all — each
|
|
399 |
+ |
//! card carries its own submit, so each is a region with a heading, a fact
|
|
400 |
+ |
//! and an act and is sayable today. A price member would have one consumer
|
|
401 |
+ |
//! and would mean growing a money type this crate does not have.
|
|
402 |
+ |
//! - **Where it is drawn is [`unavailable`](Choice::unavailable)'s question met
|
|
403 |
+ |
//! a third time**, and it takes the same answer: its own element in a radio
|
|
404 |
+ |
//! group, run into the option's own text in a `<select>`, a row of its own on
|
|
405 |
+ |
//! a terminal. A rule the crate had already paid for twice is the cheapest
|
|
406 |
+ |
//! evidence that one member is the right size.
|
|
407 |
+ |
//! - **This does not reopen 0.35.0.** That release ruled [`Candidate`] a type
|
|
408 |
+ |
//! of its own rather than a `Choice` with a second line, and it stays one:
|
|
409 |
+ |
//! what separates them is that an option is picked out of a set the user can
|
|
410 |
+ |
//! see whole and a candidate is offered out of one nobody can see. The
|
|
411 |
+ |
//! argument 0.35.0 made against a member here — that it would land on every
|
|
412 |
+ |
//! option list including the ones with nowhere to draw it — is answered by
|
|
413 |
+ |
//! the two sentences above: it lands as `None` on the ones that do not ask,
|
|
414 |
+ |
//! and the one host with nowhere to draw it already had a rule.
|
|
415 |
+ |
//!
|
| 385 |
416 |
|
//! 0.38.0 describes the theme picker. [`FieldKind::Theme`], [`ThemeChoice`],
|
| 386 |
417 |
|
//! [`ThemeVariant`], [`Contrast`], [`Field::themes`] and [`Field::follows`] are
|
| 387 |
418 |
|
//! one screen's control named as furniture. Ruled by Max 2026-08-28
|
| 3922 |
3953 |
|
///
|
| 3923 |
3954 |
|
/// Added 0.28.0, from audiofiles' instrument mode selector (`e761833e`).
|
| 3924 |
3955 |
|
pub unavailable: Option<&'a str>,
|
|
3956 |
+ |
/// The line under the label that says what picking this means.
|
|
3957 |
+ |
///
|
|
3958 |
+ |
/// `5e21dcfc`, measured 2026-08-29. A choice between three plans is a
|
|
3959 |
+ |
/// choice nobody can make from three names, and until this existed the
|
|
3960 |
+ |
/// description had nowhere to put the sentence that made it makeable. What
|
|
3961 |
+ |
/// the corpus did instead is the tell: four of the six measured sites fold
|
|
3962 |
+ |
/// it into the label — `<strong>Public</strong>: Anyone can see this
|
|
3963 |
+ |
/// repository` in MNW's git settings, the same shape in its project-basics
|
|
3964 |
+ |
/// AI tier and its cart's currency conversion, and `Mislabeled (wrong AI
|
|
3965 |
+ |
/// tier or category)` in its report modal. The described screens do it too,
|
|
3966 |
+ |
/// in miniature: `Every 15 minutes (recommended)`, `Reference samples in
|
|
3967 |
+ |
/// place (loose-files mode)`. One fact, six spellings, no member.
|
|
3968 |
+ |
///
|
|
3969 |
+ |
/// # Where it goes is the host's, and the rule already exists
|
|
3970 |
+ |
///
|
|
3971 |
+ |
/// This is [`unavailable`](Self::unavailable)'s question met a third time
|
|
3972 |
+ |
/// and it takes the same answer, which is the strongest evidence one member
|
|
3973 |
+ |
/// is right rather than two. A radio group has room and gives the line its
|
|
3974 |
+ |
/// own element beside the label. A `<select>`'s option takes no elements,
|
|
3975 |
+ |
/// no second line and no title a keyboard reaches, so the line runs into
|
|
3976 |
+ |
/// the option's own text — exactly as a precondition does, and as a theme's
|
|
3977 |
+ |
/// contrast badge does in brackets. A terminal has rows and puts it on one
|
|
3978 |
+ |
/// under the option.
|
|
3979 |
+ |
///
|
|
3980 |
+ |
/// # Not a price, and that is a measurement rather than a preference
|
|
3981 |
+ |
///
|
|
3982 |
+ |
/// The site that asked for this is MNW's fee calculator, whose tier cards
|
|
3983 |
+ |
/// carry a name, a price *and* a description, so a second member for the
|
|
3984 |
+ |
/// price was on the table. It loses on the count: the tree's other three
|
|
3985 |
+ |
/// priced tier lists — `project.html`, `project_paywall.html`,
|
|
3986 |
+ |
/// `index.html` — are not option lists at all. Each card carries its own
|
|
3987 |
+ |
/// submit, which makes it a region with a heading, a fact and an act, and
|
|
3988 |
+ |
/// it is sayable already. So a price member would have exactly one
|
|
3989 |
+ |
/// consumer, and it would mean this crate growing a money type it does not
|
|
3990 |
+ |
/// have: [`Unit`] is a time axis, and every amount in the described tree is
|
|
3991 |
+ |
/// text.
|
|
3992 |
+ |
///
|
|
3993 |
+ |
/// The price therefore leads the line: `$24/mo. 2GB/file, 100GB total.
|
|
3994 |
+ |
/// Fits audio, plugins, binaries.` What would reopen it is a **second**
|
|
3995 |
+ |
/// priced option list, not a judgement about how that reads.
|
|
3996 |
+ |
///
|
|
3997 |
+ |
/// # What it is not
|
|
3998 |
+ |
///
|
|
3999 |
+ |
/// Not [`unavailable`](Self::unavailable), which says the option cannot be
|
|
4000 |
+ |
/// picked. This says what it means to pick it, and the two are drawn
|
|
4001 |
+ |
/// together on an option that carries both: the description that says a
|
|
4002 |
+ |
/// tier is out of stock *and* what the tier is has said two things.
|
|
4003 |
+ |
///
|
|
4004 |
+ |
/// Not [`Field::hint`], which is standing help for the whole question, and
|
|
4005 |
+ |
/// not markup. One line of plain text, for [`Candidate::detail`]'s reason:
|
|
4006 |
+ |
/// an option list is a place a renderer lays out, and a description that
|
|
4007 |
+ |
/// put a block in one would be handing every host a layout problem for the
|
|
4008 |
+ |
/// benefit of one.
|
|
4009 |
+ |
///
|
|
4010 |
+ |
/// Added 0.39.0.
|
|
4011 |
+ |
pub detail: Option<&'a str>,
|
| 3925 |
4012 |
|
}
|
| 3926 |
4013 |
|
|
| 3927 |
4014 |
|
impl<'a> Choice<'a> {
|
| 3943 |
4030 |
|
value,
|
| 3944 |
4031 |
|
label,
|
| 3945 |
4032 |
|
unavailable: None,
|
|
4033 |
+ |
detail: None,
|
| 3946 |
4034 |
|
}
|
| 3947 |
4035 |
|
}
|
| 3948 |
4036 |
|
|
| 3956 |
4044 |
|
self
|
| 3957 |
4045 |
|
}
|
| 3958 |
4046 |
|
|
|
4047 |
+ |
/// The same option, with the line that says what picking it means.
|
|
4048 |
+ |
///
|
|
4049 |
+ |
/// Builder-shaped for [`unless`](Self::unless)'s reason, and it is the
|
|
4050 |
+ |
/// commoner of the two: six measured sites want this and one wants a
|
|
4051 |
+ |
/// precondition. See [`detail`](Self::detail).
|
|
4052 |
+ |
#[must_use]
|
|
4053 |
+ |
pub const fn detailing(mut self, detail: &'a str) -> Self {
|
|
4054 |
+ |
self.detail = Some(detail);
|
|
4055 |
+ |
self
|
|
4056 |
+ |
}
|
|
4057 |
+ |
|
| 3959 |
4058 |
|
/// Whether the option can be picked right now.
|
| 3960 |
4059 |
|
///
|
| 3961 |
4060 |
|
/// The predicate a renderer branches on, so that "unavailable" is read as
|
| 6896 |
6995 |
|
assert_eq!(multi.label, "Multi-sample");
|
| 6897 |
6996 |
|
}
|
| 6898 |
6997 |
|
|
|
6998 |
+ |
#[test]
|
|
6999 |
+ |
fn an_option_can_say_what_picking_it_means_and_why_it_cannot_be_picked() {
|
|
7000 |
+ |
// `5e21dcfc`. Two different sentences about one option, and an option
|
|
7001 |
+ |
// that has both has said two things: what the tier is, and that it is
|
|
7002 |
+ |
// not available yet. Folding them would be the label-folding this
|
|
7003 |
+ |
// member exists to end.
|
|
7004 |
+ |
let tier = Choice::new("24", "Small Files")
|
|
7005 |
+ |
.detailing("$24/mo. 2GB/file, 100GB total. Fits audio, plugins, binaries.")
|
|
7006 |
+ |
.unless("Sold out while the founder window is open.");
|
|
7007 |
+ |
|
|
7008 |
+ |
assert_eq!(
|
|
7009 |
+ |
tier.detail,
|
|
7010 |
+ |
Some("$24/mo. 2GB/file, 100GB total. Fits audio, plugins, binaries.")
|
|
7011 |
+ |
);
|
|
7012 |
+ |
assert_eq!(
|
|
7013 |
+ |
tier.unavailable,
|
|
7014 |
+ |
Some("Sold out while the founder window is open.")
|
|
7015 |
+ |
);
|
|
7016 |
+ |
assert!(!tier.available());
|
|
7017 |
+ |
|
|
7018 |
+ |
// Neither is implied by the other, which is what keeps a renderer from
|
|
7019 |
+ |
// reading a detail as a reason: an ordinary option with a second line
|
|
7020 |
+ |
// is still pickable.
|
|
7021 |
+ |
let plain = Choice::new("free", "Free").detailing("No charge. Available to everyone.");
|
|
7022 |
+ |
assert!(plain.available());
|
|
7023 |
+ |
assert_eq!(plain.detail, Some("No charge. Available to everyone."));
|
|
7024 |
+ |
assert_eq!(Choice::new("free", "Free").detail, None);
|
|
7025 |
+ |
}
|
|
7026 |
+ |
|
| 6899 |
7027 |
|
#[test]
|
| 6900 |
7028 |
|
fn a_range_carries_both_ends_and_a_validated_number_need_not() {
|
| 6901 |
7029 |
|
// The distinction the kind exists for, asserted rather than only
|