From 82728486202062b9f95bbfd4d679fde7ccf233b3 Mon Sep 17 00:00:00 2001 From: Taus Date: Sat, 4 Jul 2026 15:00:24 +0000 Subject: [PATCH] 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. --- shared/yeast/src/lib.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index fdfe4dd0fb0..c50304e623e 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -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` +/// / `Vec` 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 for usize { } } +impl IntoIterator for Id { + type Item = Id; + type IntoIter = std::iter::Once; + 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>` handles `Vec`, `Option`, -/// arbitrary iterators yielding `Id`, etc. +/// The blanket impl for `IntoIterator>` handles all +/// current shapes: `Vec`, `Option`, 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); } -impl IntoFieldIds for Id { - fn extend_into(self, out: &mut Vec) { - out.push(self); - } -} - impl IntoFieldIds for I where I: IntoIterator,