Skip to main content

Field

Struct Field 

Source
pub struct Field<'a> {
Show 22 fields pub kind: FieldKind, pub name: &'a str, pub upper_name: Option<&'a str>, pub label: &'a str, pub hint: Option<&'a str>, pub error: Option<&'a str>, pub note: Option<(Tone, &'a str)>, pub placeholder: Option<&'a str>, pub options: &'a [Choice<'a>], pub themes: &'a [ThemeChoice<'a>], pub follows: Option<Choice<'a>>, pub accept: &'a [Accepted<'a>], pub multiple: bool, pub required: bool, pub max_length: Option<u32>, pub min: Option<&'a str>, pub max: Option<&'a str>, pub step: Option<&'a str>, pub curve: Curve<'a>, pub unit: Option<&'a str>, pub extended: bool, pub as_instant: bool,
}

Fields§

§kind: FieldKind

What kind of value it takes.

§name: &'a str

The name the value is submitted under.

The lower end’s name for a FieldKind::Interval, whose upper end is upper_name. Every other kind submits one value and this is the whole of it.

§upper_name: Option<&'a str>

The name a FieldKind::Interval’s upper end is submitted under.

None for every other kind, and sayable-and-ignored there the way options is on a kind that offers none.

Stated rather than derived from name, and FieldKind::Interval carries the measurement that decided it: the two sites in this tree disagree about affix order, so a derived rule would rename one of them. Which member a name sits in is also what says which end it is, so nothing separate carries the direction.

An interval missing it is an interval with one end that can be submitted, which is a description a renderer may draw honestly and no better than that. Field::interval is what makes forgetting it unsayable, on the same footing as Field::range and its bounds.

Added 0.34.0.

§label: &'a str

What the user is asked for.

§hint: Option<&'a str>

Standing help, shown whether or not anything is wrong.

§error: Option<&'a str>

What is currently wrong with the value.

§note: Option<(Tone, &'a str)>

A consequence of the answer the user has given, carrying its own tone.

The third message channel, between hint and error and overlapping neither. A hint is standing help that does not depend on the value; an error says the value is not acceptable. A note is the case in the middle: the value is perfectly acceptable and choosing it costs something the user should know about.

The first consumer is audiofiles’ export Format field, where choosing WAV or AIFF over Original re-encodes and silently drops embedded BWF, iXML, loop points, cue markers and ID3. That is not a validation failure and it is not standing help — it is true of one answer to one question — and it was hand-drawn in the app’s own draw callback for want of anywhere to say it.

The tone is carried rather than fixed at Tone::Warning because the channel is not only for warnings: the same slot says “this is the recommended one” (Tone::Success) and “this is what that setting implies” (Tone::Info). A renderer gets the announcement behaviour off the tone for free — makeover-webview emits data-tone and treats Warning and Danger as assertive for aria-live.

It does not make the field invalid. invalid stays error.is_some(), so a note never marks the group as a problem.

§Precedence, for a renderer with room for one

Error, then note, then hint. A renderer that shows every message shows them in that order too. makeover-tui is the one with room for exactly one line, and it is why the order is decided here rather than three times: what is wrong outranks what it costs, which outranks how it works.

Added 0.36.0.

§placeholder: Option<&'a str>

Ghost text shown while the field is empty.

User-facing text, and it sits with label and hint rather than with the value because it is a property of the question and not of the answer. It lived renderer-side in makeover-webview until 0.8.0 for one reason and it was not a reading on where it belonged: adding a field to a published struct is a breaking change.

Not a substitute for a label. A field labelled only by its placeholder loses its label the moment anything is typed, and no renderer here can make that not happen, so the description keeps both.

§options: &'a [Choice<'a>]

The options offered, in the order they are offered.

Empty for every kind FieldKind::offers_options rejects. A field described with no options is sayable on purpose: it is what an app with an unfinished-loading option list actually has, and a renderer showing an empty control says so on screen rather than in a log.

Which option is current is not here. That is the value, and the value is renderer state.

§themes: &'a [ThemeChoice<'a>]

The themes offered, in the order they are offered.

Empty for every kind FieldKind::offers_themes rejects, and sayable as empty for the one that accepts it: an app whose theme directories hold nothing has a picker offering only follows, which is a true description of that machine.

The order is the grouping. Entries arrive sorted by ThemeVariant and then by Contrast within each variant, so a renderer that draws headings walks the run of one variant and a renderer that cannot still gets the useful order. Handing back groups would force the second renderer to flatten what the first wanted.

Nothing here sorts. The description carries the order it was given, and the sort belongs with whoever measured the tiers — makeover::theme_options is what produces it, and re-sorting here would be this crate deciding a question it cannot see the inputs to.

Which theme is current is not here. That is the value, and the value is renderer state, exactly as it is for options.

Added 0.38.0.

§follows: Option<Choice<'a>>

The entry that follows the ambient mode instead of naming a theme.

None for a picker that does not offer one, which is a real answer: an app whose host has no ambient mode to follow should not offer a row that does nothing.

A Choice rather than a bare label, because the value is the app’s. Every store in the family spells it system today and none of them is obliged to; a description that hardcoded the spelling would be this crate holding a fact about somebody else’s config table.

It is not a ThemeChoice with an absent variant. Following is a standing instruction that resolves differently as the desktop flips, and a theme id is an answer that does not — which is the distinction makeover::ThemeSelection exists to hold, carried here rather than blurred.

Added 0.38.0.

§accept: &'a [Accepted<'a>]

What a file field takes, in the order a host offering the list shows it.

Empty for every kind FieldKind::takes_files rejects, and empty is also a real answer for one that accepts it: a field that takes any file says so by listing nothing, which is what an <input type="file"> with no accept does and what most of the measured sites are.

It is a filter and it is the disclosure cue, and Accepted’s doc carries which reading is which. Nothing here validates: a host may hand back a file the list does not cover, exactly as a browser does when the user switches the picker to “All Files”, and deciding a value is wrong stays with whoever validated.

Added 0.31.0, f7261a5a.

§multiple: bool

Whether more than one file may be picked at once.

Only FieldKind::takes_files reads it. A multi-valued answer to any other question is a different shape — a set of options, a repeated group — and neither is this flag with a different kind beside it.

False is the common case: 4 of the MNW server’s 16 file inputs carry it.

Added 0.31.0, f7261a5a.

§required: bool

Whether the form refuses to submit without it.

§max_length: Option<u32>

The longest the value may be, in characters.

Added 0.11.0 with min and max, joining required, which had been the only constraint here since before the crate wrote down that it carried none.

§min: Option<&'a str>

The lowest value accepted, as the host would write it.

Text rather than a number, because the bound is only a number for some of the kinds that take one. goingson’s own sites are min="1" on a duration and min="2026-08-09T14:30" on a datetime, and a numeric member could say the first and not the second. The kind already says how to read it, the same way it does for the value.

§max: Option<&'a str>

The highest value accepted, as the host would write it. See min.

§step: Option<&'a str>

The granularity the value moves in, as the host would write it.

Text for min’s reason, and it earns it twice over: the step of a date is a day and the step of a threshold is 0.01, and a numeric member could say one of them.

Absent means the host’s own granularity, which is the honest default rather than a missing value: a webview’s <input> steps by 1 unless told otherwise, and that is the browser’s rule and not this crate’s to restate.

§It is the granularity of a typed value

FieldKind::Range reads its own from curve and ignores this, as of 0.32.0. Until then this member served both, and serving both is what the split fixes: on a slider the granularity and the mapping are one decision, and on a typed number there is no mapping to decide with. See Curve, “Why the step is here”.

Added 0.28.0 with FieldKind::Range, and narrowed away from it at 0.32.0.

§curve: Curve<'a>

How a slider’s position becomes its value, and how finely it moves.

FieldKind::Range’s, and nothing else reads it: a typed number has a granularity but no mapping, and takes step instead.

Defaults to Curve::Linear with no step, which is what every range described before 0.32.0 meant, so this member is additive and no existing site changes meaning.

Added 0.32.0.

§unit: Option<&'a str>

What the number is measured in: s, ms, dB, GiB.

A fact about the value, not part of the question’s name, and that distinction is the whole reason it is a member. The two readings come apart the moment anything reads a field back rather than drawing it: a max of -96 and a bound of -96 dBFS are the same number and not the same answer, and under the convention this replaces the unit could only be recovered by parsing it back out of a label.

§Where a renderer draws it

Beside the value, wherever that host puts a value. Not in the label: the label is the sentence above the control and that is the one place the convention could put it, which is why it read the same on every host and was wrong on the one host that had somewhere better. egui puts it inside the slider where the readout already is, a terminal appends it to the value in the edit line, a webview sets it adjacent to the input.

§Which kinds read it

FieldKind::measurable answers, and it is takes_files’s footing: three renderers ask before they can decide whether to draw this, and a matches! per renderer is where the next measurable kind goes missing. A unit on a kind that rejects it is sayable and ignored, the same way options is on a kind that offers none.

§Why a string

The measured sites are GiB, dBFS, s and ms. An enum would have to grow a member for every unit any consumer ever wants, and this crate does not know them; it knows that a number has one.

Written as the symbol alone, with no brackets and no leading space. The spacing is the renderer’s, because a slider’s readout and a sentence want different answers.

Added 0.33.0, 32215e21, on eight sites across four files that had each arrived at “Attack (s)” separately.

§extended: bool

Whether the field lives behind a “more options” disclosure.

§as_instant: bool

Whether this local wall-clock value is submitted as an absolute instant.

FieldKind::DateTime asks for a time the way a person says one – “the 14th at half past two” – and that names a different moment in Denver than it does in Berlin. A route that stores an instant needs the moment, so somebody has to convert. This member says the description wants that conversion; it does not say how.

§The conversion belongs to the renderer

Because the renderer is the only party that knows what “your computer’s time zone” means for its host. A browser has one and the user is sitting in it; a TUI reads the host clock; an egui app reads the same clock a different way. Nothing above the renderer can answer it, and the alternatives all try: a hidden IANA-zone field needs a host capability for reading the zone that three hosts answer differently, plus a kind that does not exist, plus a wire-contract change; a timezone on the user’s profile is a product decision wearing a bug’s clothes. Say it here, and the next reader does not propose them again.

§What a renderer does

Draws the same control it always did – the flag changes what is submitted, not what is shown – and converts the local value to an absolute instant on the way out. A renderer that cannot convert submits the local value unchanged, which is what every renderer did before this existed.

No wire contract moves when a site adopts it: the route was already receiving an instant. What changes is who computed it.

§Which kinds read it

FieldKind::DateTime’s. Date and Time are each half a moment and cannot name one on their own, so the flag is sayable and ignored there, the way options is on a kind that offers none.

Added 0.37.0, retiring the MNW server’s data-config="publish-at-iso" – the last site of a private per-app vocabulary that this crate exists to replace.

Implementations§

Source§

impl<'a> Field<'a>

Source

pub const fn new(kind: FieldKind, name: &'a str, label: &'a str) -> Self

A plain required-nothing field of the given kind.

Source

pub const fn range( name: &'a str, label: &'a str, min: &'a str, max: &'a str, ) -> Self

A bounded number the user drags across its whole extent.

The third under-described kind, and it gets a constructor for select’s reason: a range is the one kind whose bounds are not a rule but the control itself, so a call site that forgot them has a slider with nothing to slide across. Taking them as arguments is what makes that unsayable.

The granularity stays a field rather than a fourth argument, and since 0.32.0 it is curve’s: it is genuinely optional — the host’s own is a real answer — and the two bounds are not.

Source

pub const fn interval( name: &'a str, upper_name: &'a str, label: &'a str, ) -> Self

One question with two ends, taking the name each end submits under.

A constructor for range’s reason inverted: a range’s bounds are what a call site cannot forget, and an interval’s second name is. An interval built through new has an upper end with nowhere to be submitted, and nothing downstream can invent one, so taking it as an argument is what makes that unsayable.

The extent, the granularity and the unit stay members. They describe the axis rather than either end and they are genuinely optional, which is FieldKind::Number’s arrangement and the one an interval takes.

Source

pub const fn upload( name: &'a str, label: &'a str, accept: &'a [Accepted<'a>], ) -> Self

A file field, taking the given accept list.

The fourth under-described kind and it gets a constructor for range’s reason rather than select’s: a file field with no accept list is not broken, it is a field that takes anything, and the hazard is the opposite one. A call site that meant to restrict and forgot has a picker offering every file on the machine and a server refusing the upload afterwards, which is the failure the list exists to move forward. Taking it as an argument is what makes an accidental omission a deliberate &[].

multiple stays a field. One file is the common case and the honest default; several is the thing worth saying.

Source

pub const fn select( name: &'a str, label: &'a str, options: &'a [Choice<'a>], ) -> Self

A select offering the given options.

One of the two kinds under-described by Field::new, so it gets a constructor rather than leaving every call site to remember that a select with an empty options renders as an empty select.

Source

pub const fn radio( name: &'a str, label: &'a str, options: &'a [Choice<'a>], ) -> Self

A radio group offering the given options.

The other. Same hazard as select and a worse one: a radio group with no options draws nothing at all, so a call site that forgot them has an empty rectangle rather than a visibly empty control.

Source

pub const fn theme( name: &'a str, label: &'a str, themes: &'a [ThemeChoice<'a>], ) -> Self

A theme picker over the themes the host resolved.

A constructor for select’s reason and one of its own. The shared reason: a theme picker built through new has an empty themes list and draws an empty control. Its own: the list is the only thing this kind takes that a call site cannot get wrong by omission and can get wrong by substitution, since options is right there and reads as if it would work.

following is the builder rather than a fourth argument, because a picker with no follow-the-system row is a real picker and every renderer draws it honestly.

Source

pub const fn following(self, follow: Choice<'a>) -> Self

The same picker, offering a row that tracks the ambient mode.

The Choice carries the value the app’s own store spells it with.

Source

pub const fn invalid(&self) -> bool

Whether the field is currently reporting a problem.

Read this rather than testing error.is_some() at each renderer: the error state has to mark the field’s whole group and not only the message, because a renderer with no descendant selectors (egui, a terminal) cannot find the group from the message. goingson already marks the group and Balanced Breakfast does not, so goingson’s shape is the one taken here.

note is deliberately not consulted. A note says the answer costs something, not that it is unacceptable, and a field the user may submit as it stands is not invalid.

Source

pub const fn bounded(&self) -> bool

Whether the field carries both ends of its extent.

Only FieldKind::Range owes them, and it owes them absolutely: a slider with one end missing has no extent to draw. Named here rather than left to each renderer to test min.is_some() && max.is_some(), which is three renderers arriving at the same condition and one of them getting it wrong, and named as a question about the field rather than about the kind because the kind cannot see the bounds.

It is a check and not a guarantee. Nothing here refuses to build an unbounded range — Field::range is what makes the bounded one easy — so a renderer asks this and falls back to whatever its host does honestly with a number.

Source

pub fn accepts_media(&self) -> bool

Whether anything in accept names a media family.

The question a renderer asks before it decides to keep room for a preview, and it is deliberately the whole list rather than one entry: the media dropzone this was measured against takes image/*,video/*, so there is no single family to return and there is still a disclosure to offer. Which one it turns out to be is known once a file is picked, which is renderer-side and after the description is gone.

False for an empty list, for a list of suffixes, and for text/csv. A renderer that wants the family of a particular entry reads Accepted::family.

Trait Implementations§

Source§

impl<'a> Clone for Field<'a>

Source§

fn clone(&self) -> Field<'a>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Copy for Field<'a>

Source§

impl<'a> Debug for Field<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'a> Eq for Field<'a>

Source§

impl<'a> Hash for Field<'a>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<'a> PartialEq for Field<'a>

Source§

fn eq(&self, other: &Field<'a>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<'a> StructuralPartialEq for Field<'a>

Auto Trait Implementations§

§

impl<'a> Freeze for Field<'a>

§

impl<'a> RefUnwindSafe for Field<'a>

§

impl<'a> Send for Field<'a>

§

impl<'a> Sync for Field<'a>

§

impl<'a> Unpin for Field<'a>

§

impl<'a> UnsafeUnpin for Field<'a>

§

impl<'a> UnwindSafe for Field<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.