use crate::State;
use crate::de::{Deserialize, DeserializeDriver, Sink, SinkHandle};
use crate::error::{Error, ErrorKind};
use crate::event::{Atom, ContainerShape, Event};
use crate::extensions::Snapshot;
use crate::ser::{Emit, MapEmitter, SeqEmitter, Serialize, SerializeHandle};
use alloc::borrow::Cow;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::vec::Vec;
#[derive(Clone, Default)]
pub struct Recording(RecordBuf<'static>);
#[derive(Clone, Default)]
pub struct RecordBuf<'de> {
events: Events<'de>,
is_map_key: bool,
}
#[derive(Clone)]
enum Events<'de> {
Inline(Option<RecordedEvent<'de>>),
Heap(Vec<RecordedEvent<'de>>),
}
impl Default for Events<'_> {
fn default() -> Self {
Events::Inline(None)
}
}
impl<'de> Events<'de> {
#[inline]
fn as_slice(&self) -> &[RecordedEvent<'de>] {
match self {
Events::Inline(None) => &[],
Events::Inline(Some(event)) => core::slice::from_ref(event),
Events::Heap(events) => events,
}
}
#[inline]
fn as_mut_slice(&mut self) -> &mut [RecordedEvent<'de>] {
match self {
Events::Inline(None) => &mut [],
Events::Inline(Some(event)) => core::slice::from_mut(event),
Events::Heap(events) => events,
}
}
#[inline]
fn push(&mut self, event: RecordedEvent<'de>) {
match self {
Events::Inline(slot @ None) => *slot = Some(event),
Events::Heap(events) => events.push(event),
Events::Inline(Some(_)) => {
self.reserve(CONTAINER_CAPACITY);
if let Events::Heap(events) = self {
events.push(event);
}
}
}
}
#[inline]
fn clear(&mut self) {
match self {
Events::Inline(slot) => *slot = None,
Events::Heap(events) => events.clear(),
}
}
fn reserve(&mut self, additional: usize) {
match self {
Events::Inline(slot) => {
let mut events = Vec::with_capacity(additional + 1);
events.extend(slot.take());
*self = Events::Heap(events);
}
Events::Heap(events) => events.reserve(additional),
}
}
}
impl<'de> core::ops::Deref for Events<'de> {
type Target = [RecordedEvent<'de>];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl core::fmt::Debug for Events<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(self.as_slice(), f)
}
}
#[derive(Debug, Clone)]
struct RecordedEvent<'de> {
event: Event<'de>,
borrowed: bool,
span: u32,
input_range: (usize, usize),
snapshot: Option<Box<Snapshot>>,
}
impl RecordedEvent<'_> {
fn restore(&self, state: &mut State) {
match self.snapshot {
Some(ref snapshot) => state.extensions_mut().restore(snapshot),
None => state.clear_event_data(),
}
}
}
#[cfg(target_pointer_width = "64")]
const _: () = assert!(core::mem::size_of::<RecordedEvent<'static>>() == 64);
const _: () = {
const fn assert_send<T: Send>() {}
assert_send::<Recording>();
assert_send::<RecordBuf<'static>>();
};
impl core::fmt::Debug for Recording {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Recording")
.field("events", &self.0.events)
.field("is_map_key", &self.0.is_map_key)
.finish()
}
}
impl core::fmt::Debug for RecordBuf<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RecordBuf")
.field("events", &self.events)
.field("is_map_key", &self.is_map_key)
.finish()
}
}
trait Target<'de>: Send {
fn push(&mut self, is_root: bool, event: Event<'static>, state: &State);
fn push_borrowed(&mut self, is_root: bool, atom: Atom<'de>, state: &State);
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn close(&mut self, start: usize);
fn reserve(&mut self);
}
const CONTAINER_CAPACITY: usize = 16;
impl<'de> Target<'de> for RecordBuf<'de> {
fn push(&mut self, is_root: bool, event: Event<'static>, state: &State) {
record(self, is_root, event, false, state);
}
fn push_borrowed(&mut self, is_root: bool, atom: Atom<'de>, state: &State) {
record(self, is_root, Event::Atom(atom), true, state);
}
fn is_empty(&self) -> bool {
self.events.is_empty()
}
fn len(&self) -> usize {
self.events.len()
}
fn close(&mut self, start: usize) {
close(self.events.as_mut_slice(), start);
}
fn reserve(&mut self) {
self.events.reserve(CONTAINER_CAPACITY);
}
}
impl<'de> Target<'de> for Recording {
fn push(&mut self, is_root: bool, event: Event<'static>, state: &State) {
record(&mut self.0, is_root, event, false, state);
}
fn push_borrowed(&mut self, is_root: bool, atom: Atom<'de>, state: &State) {
let event = Event::Atom(atom.to_static());
record(&mut self.0, is_root, event, false, state);
}
fn is_empty(&self) -> bool {
self.0.events.is_empty()
}
fn len(&self) -> usize {
self.0.events.len()
}
fn close(&mut self, start: usize) {
close(self.0.events.as_mut_slice(), start);
}
fn reserve(&mut self) {
self.0.events.reserve(CONTAINER_CAPACITY);
}
}
impl Recording {
pub fn new() -> Recording {
Recording::default()
}
pub fn recorder<'de>(&mut self, state: &mut State) -> SinkHandle<'_, 'de> {
self.0.events.clear();
self.0.is_map_key = false;
SinkHandle::arena(
Recorder {
target: self,
end: None,
start: 0,
is_root: true,
},
state,
)
}
pub fn capture<'a, 'de, F>(then: F, state: &mut State) -> SinkHandle<'a, 'de>
where
F: FnOnce(Recording, &mut State) -> Result<(), Error> + Send + 'a,
{
SinkHandle::arena(
CaptureSink {
recording: Recording::new(),
end: None,
then: Some(FnCapture(Some(then))),
},
state,
)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn events(&self) -> impl Iterator<Item = &Event<'static>> {
self.0.events.iter().map(|recorded| &recorded.event)
}
pub fn as_str(&self) -> Option<&str> {
self.0.as_str()
}
pub fn replay<'de>(&self, sink: SinkHandle<'_, 'de>, state: &mut State) -> Result<(), Error> {
self.0.replay(sink, state)
}
}
impl<'de> RecordBuf<'de> {
pub fn new() -> RecordBuf<'de> {
RecordBuf::default()
}
pub fn recorder(&mut self, state: &mut State) -> SinkHandle<'_, 'de> {
self.events.clear();
self.is_map_key = false;
SinkHandle::arena(
Recorder {
target: self,
end: None,
start: 0,
is_root: true,
},
state,
)
}
pub fn capture<'a, F>(then: F, state: &mut State) -> SinkHandle<'a, 'de>
where
F: FnOnce(RecordBuf<'de>, &mut State) -> Result<(), Error> + Send + 'a,
'de: 'a,
{
RecordBuf::capture_with(FnCapture(Some(then)), state)
}
#[cfg_attr(not(feature = "derive"), allow(dead_code))]
pub(crate) fn capture_with<'a, C: Capture<'de, RecordBuf<'de>> + 'a>(
then: C,
state: &mut State,
) -> SinkHandle<'a, 'de>
where
'de: 'a,
{
SinkHandle::arena(
CaptureSink {
recording: RecordBuf::new(),
end: None,
then: Some(then),
},
state,
)
}
#[cfg_attr(not(feature = "derive"), allow(dead_code))]
pub(crate) fn set_atom(&mut self, atom: &Atom<'_>, state: &State) {
self.events.clear();
self.is_map_key = false;
record(self, true, Event::Atom(atom.to_static()), false, state);
}
#[cfg_attr(not(feature = "derive"), allow(dead_code))]
pub(crate) fn offset(&self) -> Option<usize> {
self.events
.first()
.map(|x| x.input_range.0)
.filter(|&x| x != crate::state::NO_RANGE.0)
}
#[cfg_attr(not(feature = "derive"), allow(dead_code))]
pub(crate) fn attach_context(&self, err: Error, state: &mut State) -> Error {
let Some(first) = self.events.first() else {
return state.error_in_context(err);
};
let live = state.extensions().snapshot();
let live_range = state.input_range;
state.input_range = first.input_range;
first.restore(state);
let err = state.error_in_context(err);
state.extensions_mut().restore(&live);
state.input_range = live_range;
err
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
#[cfg_attr(not(feature = "derive"), allow(dead_code))]
pub(crate) fn single_atom(&self) -> Option<&Atom<'de>> {
match self.events.as_slice() {
[
RecordedEvent {
event: Event::Atom(atom),
..
},
] => Some(atom),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
self.single_atom()?.as_str()
}
pub fn replay<'a>(&self, sink: SinkHandle<'_, 'a>, state: &mut State) -> Result<(), Error>
where
'de: 'a,
{
let live = state.extensions().snapshot_if_any();
let live_range = state.input_range;
let rv = self.replay_events(sink, state);
match live {
Some(live) => state.extensions_mut().restore(&live),
None => state.clear_event_data(),
}
state.input_range = live_range;
rv
}
fn replay_events<'a>(&self, sink: SinkHandle<'_, 'a>, state: &mut State) -> Result<(), Error>
where
'de: 'a,
{
DeserializeDriver::nested(state, sink, self.is_map_key, |driver| {
for recorded in self.events.iter() {
let state = driver.state_mut();
state.input_range = recorded.input_range;
recorded.restore(state);
match recorded.event {
Event::Atom(ref atom) if recorded.borrowed => {
driver.emit_borrowed(Event::Atom(atom.clone()))?
}
ref event => driver.emit(event.as_borrowed())?,
}
}
Ok(())
})
}
}
fn record<'de>(
buf: &mut RecordBuf<'de>,
is_root: bool,
event: Event<'de>,
borrowed: bool,
state: &State,
) {
if is_root && buf.events.is_empty() {
buf.is_map_key = state.is_map_key();
}
let recorded = RecordedEvent {
span: match event {
Event::Atom(_) => 1,
_ => 0,
},
event,
borrowed,
input_range: state.input_range,
snapshot: state.extensions().snapshot_if_any().map(Box::new),
};
match buf.events {
Events::Heap(ref mut events) => events.push(recorded),
ref mut events => events.push(recorded),
}
}
pub(crate) trait Capture<'de, T>: Send {
fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error>;
fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
self.atom(atom, state)
}
fn recorded(&mut self, recording: T, state: &mut State) -> Result<(), Error>;
fn expecting(&self) -> Cow<'_, str> {
Cow::Borrowed("any value")
}
}
struct FnCapture<F>(Option<F>);
impl<'de, T, F> Capture<'de, T> for FnCapture<F>
where
T: Target<'de> + Default,
F: FnOnce(T, &mut State) -> Result<(), Error> + Send,
{
fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
let mut recording = T::default();
recording.push(true, Event::Atom(atom.to_static()), state);
self.recorded(recording, state)
}
fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
let mut recording = T::default();
recording.push_borrowed(true, atom, state);
self.recorded(recording, state)
}
fn recorded(&mut self, recording: T, state: &mut State) -> Result<(), Error> {
match self.0.take() {
Some(then) => then(recording, state),
None => Ok(()),
}
}
}
struct CaptureSink<T, C> {
recording: T,
end: Option<Event<'static>>,
then: Option<C>,
}
impl<'de, T, C> CaptureSink<T, C> {
fn child(&mut self, state: &mut State) -> SinkHandle<'_, 'de>
where
T: Target<'de>,
{
SinkHandle::arena(
Recorder {
target: &mut self.recording,
end: None,
start: 0,
is_root: false,
},
state,
)
}
}
impl<'de, T: Target<'de> + Default, C: Capture<'de, T>> Sink<'de> for CaptureSink<T, C> {
fn expecting(&self) -> Cow<'_, str> {
match self.then {
Some(ref then) => then.expecting(),
None => Cow::Borrowed("any value"),
}
}
fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
match self.then.take() {
Some(mut then) => then.atom(atom, state),
None => Ok(()),
}
}
fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
match self.then.take() {
Some(mut then) => then.borrowed_atom(atom, state),
None => Ok(()),
}
}
fn map(&mut self, state: &mut State) -> Result<(), Error> {
let shape = state.container_shape();
self.recording.reserve();
self.recording.push(true, Event::MapStart(shape), state);
self.end = Some(Event::MapEnd);
Ok(())
}
fn seq(&mut self, state: &mut State) -> Result<(), Error> {
let shape = state.container_shape();
self.recording.reserve();
self.recording.push(true, Event::SeqStart(shape), state);
self.end = Some(Event::SeqEnd);
Ok(())
}
fn next_key(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
Ok(self.child(state))
}
fn next_value(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
Ok(self.child(state))
}
fn __private_key_atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
self.recording
.push(false, Event::Atom(atom.to_static()), state);
Ok(())
}
fn __private_value_atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
self.recording
.push(false, Event::Atom(atom.to_static()), state);
Ok(())
}
fn __private_borrowed_key_atom(
&mut self,
atom: Atom<'de>,
state: &mut State,
) -> Result<(), Error> {
self.recording.push_borrowed(false, atom, state);
Ok(())
}
fn __private_borrowed_value_atom(
&mut self,
atom: Atom<'de>,
state: &mut State,
) -> Result<(), Error> {
self.recording.push_borrowed(false, atom, state);
Ok(())
}
fn value_for_key(
&mut self,
key: &str,
state: &mut State,
) -> Result<Option<SinkHandle<'_, 'de>>, Error> {
match self.end {
Some(Event::MapEnd) => {}
None if self.recording.is_empty() => {
self.recording
.push(true, Event::MapStart(ContainerShape::new()), state);
self.end = Some(Event::MapEnd);
}
_ => return Ok(None),
}
self.recording
.push(false, Event::Atom(Atom::Str(key.to_owned().into())), state);
Ok(Some(self.child(state)))
}
fn finish(&mut self, state: &mut State) -> Result<(), Error> {
let Some(mut then) = self.then.take() else {
return Ok(());
};
if self.recording.is_empty() && self.end.is_none() {
self.recording
.push(true, Event::MapStart(ContainerShape::new()), state);
self.end = Some(Event::MapEnd);
}
if let Some(end) = self.end.take() {
self.recording.push(true, end, state);
self.recording.close(0);
}
then.recorded(core::mem::take(&mut self.recording), state)
}
}
struct Recorder<'a, T> {
target: &'a mut T,
end: Option<Event<'static>>,
start: usize,
is_root: bool,
}
impl<'a, T> Recorder<'a, T> {
fn child<'de>(&mut self, state: &mut State) -> SinkHandle<'_, 'de>
where
T: Target<'de>,
{
SinkHandle::arena(
Recorder {
target: &mut *self.target,
end: None,
start: 0,
is_root: false,
},
state,
)
}
}
impl<'a, 'de, T: Target<'de>> Sink<'de> for Recorder<'a, T> {
fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
self.target
.push(self.is_root, Event::Atom(atom.to_static()), state);
Ok(())
}
fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
self.target.push_borrowed(self.is_root, atom, state);
Ok(())
}
fn map(&mut self, state: &mut State) -> Result<(), Error> {
self.start = self.target.len();
self.target.push(
self.is_root,
Event::MapStart(state.container_shape()),
state,
);
self.end = Some(Event::MapEnd);
Ok(())
}
fn seq(&mut self, state: &mut State) -> Result<(), Error> {
self.start = self.target.len();
self.target.push(
self.is_root,
Event::SeqStart(state.container_shape()),
state,
);
self.end = Some(Event::SeqEnd);
Ok(())
}
fn next_key(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
Ok(self.child(state))
}
fn next_value(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
Ok(self.child(state))
}
fn __private_key_atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
self.target
.push(false, Event::Atom(atom.to_static()), state);
Ok(())
}
fn __private_value_atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
self.target
.push(false, Event::Atom(atom.to_static()), state);
Ok(())
}
fn __private_borrowed_key_atom(
&mut self,
atom: Atom<'de>,
state: &mut State,
) -> Result<(), Error> {
self.target.push_borrowed(false, atom, state);
Ok(())
}
fn __private_borrowed_value_atom(
&mut self,
atom: Atom<'de>,
state: &mut State,
) -> Result<(), Error> {
self.target.push_borrowed(false, atom, state);
Ok(())
}
fn finish(&mut self, state: &mut State) -> Result<(), Error> {
if let Some(end) = self.end.take() {
self.target.push(self.is_root, end, state);
self.target.close(self.start);
}
Ok(())
}
}
impl PartialEq for Recording {
fn eq(&self, other: &Self) -> bool {
self.0.events.len() == other.0.events.len()
&& self
.0
.events
.iter()
.zip(other.0.events.iter())
.all(|(a, b)| a.event == b.event)
}
}
impl<'de> Deserialize<'de> for Recording {
fn deserialize_into<'out>(
out: &'out mut Option<Self>,
state: &mut State,
) -> SinkHandle<'out, 'de> {
Recording::capture(
move |recording, _state| {
*out = Some(recording);
Ok(())
},
state,
)
}
fn expecting() -> Cow<'static, str> {
Cow::Borrowed("any value")
}
}
#[inline]
fn value_len(events: &[RecordedEvent<'_>]) -> usize {
match events.first() {
Some(first) if first.span != 0 => first.span as usize,
_ => scan_value_len(events),
}
}
#[cold]
fn scan_value_len(events: &[RecordedEvent<'_>]) -> usize {
let mut depth = 0usize;
for (index, recorded) in events.iter().enumerate() {
match recorded.event {
Event::MapStart(_) | Event::SeqStart(_) => depth += 1,
Event::MapEnd | Event::SeqEnd => depth = depth.saturating_sub(1),
Event::Atom(_) => {}
}
if depth == 0 {
return index + 1;
}
}
events.len()
}
fn close(events: &mut [RecordedEvent<'_>], start: usize) {
let Some((head, rest)) = events.get_mut(start..).and_then(|x| x.split_first_mut()) else {
return;
};
head.span = u32::try_from(rest.len() + 1).unwrap_or(0);
let is_map = match head.event {
Event::MapStart(shape) if shape.len().is_none() => true,
Event::SeqStart(shape) if shape.len().is_none() => false,
_ => return,
};
let inner = &rest[..rest.len().saturating_sub(1)];
let mut count = 0;
let mut index = 0;
while index < inner.len() {
index += value_len(&inner[index..]);
count += 1;
}
if let Event::MapStart(ref mut shape) | Event::SeqStart(ref mut shape) = head.event {
shape.set_len(if is_map { count / 2 } else { count });
}
}
struct RecordedValue<'a, 'de>(&'a [RecordedEvent<'de>]);
impl<'a, 'de> RecordedValue<'a, 'de> {
fn emit(&self, state: &mut State) -> Result<Emit<'a>, Error> {
let events = self.0;
let (first, snapshot) = match events.first() {
Some(first) => (&first.event, &first.snapshot),
None => {
return Err(Error::new(
ErrorKind::InvalidState,
"cannot serialize an empty recording",
));
}
};
if let Some(snapshot) = snapshot {
state.attach_event_data(snapshot.event_data());
}
let inner = events.get(1..events.len().saturating_sub(1)).unwrap_or(&[]);
Ok(match first {
Event::Atom(atom @ Atom::Ext(_)) => {
crate::ext::raw::serialize_recorded_atom(atom, state)?
}
Event::Atom(atom) => Emit::Atom(atom.as_borrowed()),
Event::MapStart(_) => Emit::map(
RecordedEmitter {
rest: inner,
current: RecordedValue(&[]),
},
state,
),
Event::SeqStart(_) => Emit::seq(
RecordedEmitter {
rest: inner,
current: RecordedValue(&[]),
},
state,
),
Event::MapEnd | Event::SeqEnd => {
return Err(Error::new(ErrorKind::InvalidState, "malformed recording"));
}
})
}
fn container_shape(&self) -> ContainerShape {
match self.0.first().map(|x| &x.event) {
Some(Event::MapStart(shape) | Event::SeqStart(shape)) => *shape,
_ => ContainerShape::new(),
}
}
fn is_optional(&self) -> bool {
matches!(
self.0,
[RecordedEvent {
event: Event::Atom(Atom::Null),
..
}]
)
}
}
impl Serialize for RecordedValue<'_, '_> {
fn serialize<'a>(value: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
value.emit(state)
}
fn container_shape(value: &Self) -> ContainerShape {
RecordedValue::container_shape(value)
}
}
struct RecordedEmitter<'a, 'de> {
rest: &'a [RecordedEvent<'de>],
current: RecordedValue<'a, 'de>,
}
impl RecordedEmitter<'_, '_> {
fn next_value(&mut self) -> Option<SerializeHandle<'_>> {
if self.rest.is_empty() {
return None;
}
let len = value_len(self.rest);
let (value, rest) = self.rest.split_at(len);
self.current = RecordedValue(value);
self.rest = rest;
Some(SerializeHandle::to(&self.current))
}
}
impl SeqEmitter for RecordedEmitter<'_, '_> {
fn next(&mut self, _state: &mut State) -> Result<Option<SerializeHandle<'_>>, Error> {
Ok(self.next_value())
}
}
impl MapEmitter for RecordedEmitter<'_, '_> {
fn next_key(&mut self, _state: &mut State) -> Result<Option<SerializeHandle<'_>>, Error> {
Ok(self.next_value())
}
fn next_value(&mut self, _state: &mut State) -> Result<SerializeHandle<'_>, Error> {
RecordedEmitter::next_value(self)
.ok_or_else(|| Error::new(ErrorKind::InvalidState, "malformed recording"))
}
}
impl Serialize for Recording {
fn serialize<'a>(value: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
RecordedValue(value.0.events.as_slice()).emit(state)
}
fn container_shape(value: &Self) -> ContainerShape {
RecordedValue(value.0.events.as_slice()).container_shape()
}
fn is_optional(value: &Self) -> bool {
RecordedValue(value.0.events.as_slice()).is_optional()
}
}
impl Serialize for RecordBuf<'_> {
fn serialize<'a>(value: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
RecordedValue(value.events.as_slice()).emit(state)
}
fn container_shape(value: &Self) -> ContainerShape {
RecordedValue(value.events.as_slice()).container_shape()
}
fn is_optional(value: &Self) -> bool {
RecordedValue(value.events.as_slice()).is_optional()
}
}
impl<'de> Deserialize<'de> for RecordBuf<'de> {
fn deserialize_into<'out>(
out: &'out mut Option<Self>,
state: &mut State,
) -> SinkHandle<'out, 'de> {
RecordBuf::capture(
move |buf, _state| {
*out = Some(buf);
Ok(())
},
state,
)
}
fn expecting() -> Cow<'static, str> {
Cow::Borrowed("any value")
}
}