yeast: Implement IntoIterator for Id as a singleton

There's an awkward divide in yeast between returning a list of nodes or
optional node (both of which are iterable), and returning a single node
(which is not). In practice, we would like all of these cases to be
handled transparently: if a single node is returned, it behaves as if it
were a singleton list containing that node. This gives us a uniform
interface during translation -- no matter what is returned, it will be
an iterable of nodes.

To facilitate this, we make the slightly unorthodox choice of
implementing IntoIterator for Id, with the behaviour detailed above.
This commit is contained in:
Taus
2026-07-04 15:00:24 +00:00
parent 3c3f740a25
commit 8272848620

View File

@@ -26,6 +26,12 @@ use query::QueryNode;
/// without colliding with the impls for plain integers.
///
/// Use `id.0` (or `id.into()`) to obtain the raw arena index.
///
/// Implements [`IntoIterator`] as a singleton (`iter::once(self)`)
/// so that a bare `Id` can be used interchangeably with `Option<Id>`
/// / `Vec<Id>` in places that expect an iterable of ids (e.g.
/// [`crate::build::BuildCtx::translate`] and the field-splice
/// interpolation via [`IntoFieldIds`]).
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Serialize)]
pub struct Id(pub usize);
@@ -42,6 +48,14 @@ impl From<Id> for usize {
}
}
impl IntoIterator for Id {
type Item = Id;
type IntoIter = std::iter::Once<Id>;
fn into_iter(self) -> Self::IntoIter {
std::iter::once(self)
}
}
/// Field and Kind ids are provided by tree-sitter
type FieldId = u16;
type KindId = u16;
@@ -49,9 +63,10 @@ type KindId = u16;
/// Trait for values that can be appended to a field's id list inside a
/// `tree!`/`trees!`/`rule!` template (in `{expr}` placeholders).
///
/// `Id` pushes a single id; the blanket impl for
/// `IntoIterator<Item: Into<Id>>` handles `Vec<Id>`, `Option<Id>`,
/// arbitrary iterators yielding `Id`, etc.
/// The blanket impl for `IntoIterator<Item: Into<Id>>` handles all
/// current shapes: `Vec<Id>`, `Option<Id>`, arbitrary iterators
/// yielding `Id`, and a bare `Id` itself (which is `IntoIterator`
/// via a singleton).
///
/// This lets `{expr}` interpolate any of these shapes without a
/// dedicated splice syntax — the macro emits the same trait-dispatched
@@ -60,12 +75,6 @@ pub trait IntoFieldIds {
fn extend_into(self, out: &mut Vec<Id>);
}
impl IntoFieldIds for Id {
fn extend_into(self, out: &mut Vec<Id>) {
out.push(self);
}
}
impl<I, T> IntoFieldIds for I
where
I: IntoIterator<Item = T>,