@@ -136,6 +136,95 @@ pub(super) enum Generator {
136136 /// !timestamp
137137 /// ```
138138 Timestamp ,
139+
140+ /// Generate a JSON array whose elements are drawn from a sub-generator.
141+ ///
142+ /// The number of elements is controlled by the `length` field, which
143+ /// accepts three forms:
144+ ///
145+ /// - A plain integer: every array has exactly that many elements.
146+ /// - A range mapping `{min: N, max: M}`: the count is drawn uniformly
147+ /// from `[N, M]` (inclusive) on each call.
148+ /// - A sequence of integers `[N, M, ...]`: one length is chosen uniformly
149+ /// at random from the list on each call.
150+ ///
151+ /// ```yaml
152+ /// # Fixed length
153+ /// !array
154+ /// length: 3
155+ /// element: !range { min: 1, max: 100 }
156+ ///
157+ /// # Uniform random length
158+ /// !array
159+ /// length: { min: 1, max: 5 }
160+ /// element: !const "hello"
161+ ///
162+ /// # Choose length from a set
163+ /// !array
164+ /// length: [2, 3, 5]
165+ /// element: !reference my_def
166+ /// ```
167+ Array ( ArraySpec ) ,
168+ }
169+
170+ // ── Array types ───────────────────────────────────────────────────────────────
171+
172+ /// Determines how many elements a `!array` generator produces per call.
173+ ///
174+ /// Deserializes from three YAML forms:
175+ /// - scalar integer → `Fixed`
176+ /// - sequence → `Choose`
177+ /// - mapping → `Range`
178+ #[ derive( Clone ) ]
179+ pub ( super ) enum ArrayLength {
180+ /// Always produce exactly `n` elements.
181+ Fixed ( usize ) ,
182+ /// Draw the count uniformly from `[min, max]` inclusive.
183+ Range { min : usize , max : usize } ,
184+ /// Choose the count uniformly from the given list of values.
185+ Choose ( Vec < usize > ) ,
186+ }
187+
188+ impl < ' de > Deserialize < ' de > for ArrayLength {
189+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
190+ where
191+ D : serde:: Deserializer < ' de > ,
192+ {
193+ // Untagged: integer -> Fixed, sequence -> Choose, mapping -> Range.
194+ #[ derive( Deserialize ) ]
195+ #[ serde( untagged) ]
196+ enum RawArrayLength {
197+ Fixed ( usize ) ,
198+ Choose ( Vec < usize > ) ,
199+ Range { min : usize , max : usize } ,
200+ }
201+ match RawArrayLength :: deserialize ( deserializer) ? {
202+ RawArrayLength :: Fixed ( n) => Ok ( Self :: Fixed ( n) ) ,
203+ RawArrayLength :: Choose ( choices) => {
204+ if choices. is_empty ( ) {
205+ return Err ( <D :: Error as serde:: de:: Error >:: custom (
206+ "!array length list must not be empty" ,
207+ ) ) ;
208+ }
209+ Ok ( Self :: Choose ( choices) )
210+ }
211+ RawArrayLength :: Range { min, max } => {
212+ if min > max {
213+ return Err ( <D :: Error as serde:: de:: Error >:: custom ( format ! (
214+ "!array length range min ({min}) must be <= max ({max})"
215+ ) ) ) ;
216+ }
217+ Ok ( Self :: Range { min, max } )
218+ }
219+ }
220+ }
221+ }
222+
223+ /// Configuration for a `!array` generator node.
224+ #[ derive( Clone , Deserialize ) ]
225+ pub ( super ) struct ArraySpec {
226+ pub length : ArrayLength ,
227+ pub element : Box < Generator > ,
139228}
140229
141230/// Return the JSON string body encoding of `s`: the bytes that would appear
@@ -231,11 +320,11 @@ impl<'de> Deserialize<'de> for RangeSpec {
231320 {
232321 #[ derive( Deserialize ) ]
233322 #[ serde( deny_unknown_fields) ]
234- struct Raw {
323+ struct RawRangeSpec {
235324 min : i64 ,
236325 max : i64 ,
237326 }
238- let raw = Raw :: deserialize ( deserializer) ?;
327+ let raw = RawRangeSpec :: deserialize ( deserializer) ?;
239328 if raw. min > raw. max {
240329 return Err ( <D :: Error as serde:: de:: Error >:: custom ( format ! (
241330 "!range min ({}) must be <= max ({})" ,
@@ -279,11 +368,11 @@ impl<'de> Deserialize<'de> for FormatSpec {
279368 {
280369 #[ derive( Deserialize ) ]
281370 #[ serde( deny_unknown_fields) ]
282- struct Raw {
371+ struct RawFormatSpec {
283372 template : String ,
284373 args : Vec < Generator > ,
285374 }
286- let raw = Raw :: deserialize ( deserializer) ?;
375+ let raw = RawFormatSpec :: deserialize ( deserializer) ?;
287376
288377 // Split at each `{}` placeholder to get N+1 literal parts.
289378 let parts: Vec < & str > = raw. template . split ( "{}" ) . collect ( ) ;
0 commit comments