1use std::borrow::Cow;
2use std::fmt::Debug;
3
4use serde::Serialize;
5use utoipa::openapi::path::ParameterStyle;
6use utoipa::openapi::{RefOr, Schema};
7use utoipa::{PartialSchema, ToSchema};
8
9use super::ApiClientError;
10
11pub trait ParameterValue: Serialize + ToSchema + Debug + Send + Sync + Clone + 'static {}
16
17impl<T> ParameterValue for T where T: Serialize + ToSchema + Debug + Send + Sync + Clone + 'static {}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum ParamStyle {
49 Default,
51 Form,
53 Simple,
55 SpaceDelimited,
57 PipeDelimited,
59}
60
61impl From<ParamStyle> for Option<ParameterStyle> {
62 fn from(value: ParamStyle) -> Self {
63 let result = match value {
64 ParamStyle::Default => return None,
65 ParamStyle::Form => ParameterStyle::Form,
66 ParamStyle::Simple => ParameterStyle::Simple,
67 ParamStyle::SpaceDelimited => ParameterStyle::SpaceDelimited,
68 ParamStyle::PipeDelimited => ParameterStyle::PipeDelimited,
69 };
70 Some(result)
71 }
72}
73
74#[derive(Debug, Clone)]
76pub struct ParamValue<T>
77where
78 T: Serialize + ToSchema + Debug + Send + Sync + Clone,
79{
80 pub value: T,
82 pub style: ParamStyle,
84}
85
86#[derive(Debug, Clone)]
91pub(super) struct ResolvedParamValue {
92 pub value: serde_json::Value,
94 pub schema: RefOr<Schema>,
96 pub style: ParamStyle,
98}
99
100impl<T> ParamValue<T>
101where
102 T: Serialize + ToSchema + Debug + Send + Sync + Clone,
103{
104 pub fn new(value: T) -> Self {
106 Self {
107 value,
108 style: ParamStyle::Default,
109 }
110 }
111
112 pub fn with_style(value: T, style: ParamStyle) -> Self {
114 Self { value, style }
115 }
116
117 pub fn query_style(&self) -> ParamStyle {
119 match self.style {
120 ParamStyle::Default => ParamStyle::Form,
121 style => style,
122 }
123 }
124
125 pub fn path_style(&self) -> ParamStyle {
127 match self.style {
128 ParamStyle::Default => ParamStyle::Simple,
129 style => style,
130 }
131 }
132
133 pub fn header_style(&self) -> ParamStyle {
135 match self.style {
136 ParamStyle::Default => ParamStyle::Simple,
137 style => style,
138 }
139 }
140
141 pub fn as_query_value(&self) -> Option<serde_json::Value> {
145 serde_json::to_value(&self.value).ok()
146 }
147
148 pub fn as_header_value(&self) -> Result<serde_json::Value, ApiClientError> {
150 serde_json::to_value(&self.value).map_err(|e| ApiClientError::SerializationError {
151 message: format!("Failed to serialize header value: {e}"),
152 })
153 }
154
155 pub(super) fn resolve<F>(&self, add_schema_fn: F) -> Option<ResolvedParamValue>
160 where
161 F: FnOnce(serde_json::Value) -> RefOr<Schema>,
162 {
163 self.as_query_value().map(|value| {
164 let schema = add_schema_fn(value.clone());
165 ResolvedParamValue {
166 value,
167 schema,
168 style: self.style,
169 }
170 })
171 }
172}
173
174impl ResolvedParamValue {
175 fn json_value_to_string(value: &serde_json::Value) -> Result<String, ApiClientError> {
180 match value {
181 serde_json::Value::String(s) => Ok(s.clone()),
182 serde_json::Value::Number(n) => Ok(n.to_string()),
183 serde_json::Value::Bool(b) => Ok(b.to_string()),
184 serde_json::Value::Null => Ok(String::new()),
185 serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
186 Err(ApiClientError::UnsupportedParameterValue {
187 message: "nested complex values not supported in parameters".to_string(),
188 value: value.clone(),
189 })
190 }
191 }
192 }
193
194 fn array_to_string_values(arr: &[serde_json::Value]) -> Result<Vec<String>, ApiClientError> {
199 arr.iter()
200 .map(Self::json_value_to_string)
201 .collect::<Result<Vec<_>, _>>()
202 }
203
204 pub(super) fn to_string_value(&self) -> Result<String, ApiClientError> {
214 match &self.value {
215 serde_json::Value::Array(arr) => {
216 let string_values = Self::array_to_string_values(arr)?;
217 let delimiter = match self.style {
218 ParamStyle::Default | ParamStyle::Simple => ",",
219 ParamStyle::Form => ",", ParamStyle::SpaceDelimited => " ",
221 ParamStyle::PipeDelimited => "|",
222 };
223 Ok(string_values.join(delimiter))
224 }
225 serde_json::Value::Object(_) => Err(ApiClientError::UnsupportedParameterValue {
226 message: "object values not supported in parameters".to_string(),
227 value: self.value.clone(),
228 }),
229 _ => Self::json_value_to_string(&self.value),
230 }
231 }
232
233 pub(super) fn to_query_values(&self) -> Result<Vec<String>, ApiClientError> {
243 match &self.value {
244 serde_json::Value::Array(arr) => {
245 match self.style {
246 ParamStyle::Default | ParamStyle::Form => {
247 Self::array_to_string_values(arr)
249 }
250 _ => {
251 self.to_string_value().map(|s| vec![s])
253 }
254 }
255 }
256 serde_json::Value::Object(_) => Err(ApiClientError::UnsupportedParameterValue {
257 message: "object values not supported in parameters".to_string(),
258 value: self.value.clone(),
259 }),
260 _ => Self::json_value_to_string(&self.value).map(|s| vec![s]),
261 }
262 }
263}
264
265impl<T> From<T> for ParamValue<T>
266where
267 T: Serialize + ToSchema + Debug + Send + Sync + Clone,
268{
269 fn from(value: T) -> Self {
270 Self::new(value)
271 }
272}
273
274impl<T: ToSchema> ToSchema for ParamValue<T>
276where
277 T: Serialize + ToSchema + Debug + Send + Sync + Clone,
278{
279 fn name() -> Cow<'static, str> {
280 T::name()
281 }
282}
283
284impl<T: ToSchema> PartialSchema for ParamValue<T>
285where
286 T: Serialize + ToSchema + Debug + Send + Sync + Clone,
287{
288 fn schema() -> RefOr<Schema> {
289 T::schema()
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use serde::{Deserialize, Serialize};
297 use utoipa::ToSchema;
298 use utoipa::openapi::path::ParameterStyle;
299
300 #[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq)]
301 struct TestStruct {
302 id: u32,
303 name: String,
304 }
305
306 #[test]
308 fn test_param_style_from_conversion() {
309 assert_eq!(Option::<ParameterStyle>::from(ParamStyle::Default), None);
310 assert_eq!(
311 Option::<ParameterStyle>::from(ParamStyle::Form),
312 Some(ParameterStyle::Form)
313 );
314 assert_eq!(
315 Option::<ParameterStyle>::from(ParamStyle::Simple),
316 Some(ParameterStyle::Simple)
317 );
318 assert_eq!(
319 Option::<ParameterStyle>::from(ParamStyle::SpaceDelimited),
320 Some(ParameterStyle::SpaceDelimited)
321 );
322 assert_eq!(
323 Option::<ParameterStyle>::from(ParamStyle::PipeDelimited),
324 Some(ParameterStyle::PipeDelimited)
325 );
326 }
327
328 #[test]
329 fn test_param_style_eq() {
330 assert_eq!(ParamStyle::Default, ParamStyle::Default);
331 assert_eq!(ParamStyle::Form, ParamStyle::Form);
332 assert_ne!(ParamStyle::Form, ParamStyle::Simple);
333 }
334
335 #[test]
337 fn test_param_value_new() {
338 let param = ParamValue::new(42);
339 assert_eq!(param.value, 42);
340 assert_eq!(param.style, ParamStyle::Default);
341 }
342
343 #[test]
344 fn test_param_value_with_style() {
345 let param = ParamValue::with_style("test", ParamStyle::Form);
346 assert_eq!(param.value, "test");
347 assert_eq!(param.style, ParamStyle::Form);
348 }
349
350 #[test]
351 fn test_param_value_from_conversion() {
352 let param: ParamValue<i32> = 42.into();
353 assert_eq!(param.value, 42);
354 assert_eq!(param.style, ParamStyle::Default);
355 }
356
357 #[test]
359 fn test_param_value_query_style() {
360 let default_param = ParamValue::new(42);
361 assert_eq!(default_param.query_style(), ParamStyle::Form);
362
363 let form_param = ParamValue::with_style(42, ParamStyle::Form);
364 assert_eq!(form_param.query_style(), ParamStyle::Form);
365
366 let simple_param = ParamValue::with_style(42, ParamStyle::Simple);
367 assert_eq!(simple_param.query_style(), ParamStyle::Simple);
368
369 let space_param = ParamValue::with_style(42, ParamStyle::SpaceDelimited);
370 assert_eq!(space_param.query_style(), ParamStyle::SpaceDelimited);
371
372 let pipe_param = ParamValue::with_style(42, ParamStyle::PipeDelimited);
373 assert_eq!(pipe_param.query_style(), ParamStyle::PipeDelimited);
374 }
375
376 #[test]
377 fn test_param_value_path_style() {
378 let default_param = ParamValue::new(42);
379 assert_eq!(default_param.path_style(), ParamStyle::Simple);
380
381 let form_param = ParamValue::with_style(42, ParamStyle::Form);
382 assert_eq!(form_param.path_style(), ParamStyle::Form);
383
384 let simple_param = ParamValue::with_style(42, ParamStyle::Simple);
385 assert_eq!(simple_param.path_style(), ParamStyle::Simple);
386 }
387
388 #[test]
389 fn test_param_value_header_style() {
390 let default_param = ParamValue::new(42);
391 assert_eq!(default_param.header_style(), ParamStyle::Simple);
392
393 let form_param = ParamValue::with_style(42, ParamStyle::Form);
394 assert_eq!(form_param.header_style(), ParamStyle::Form);
395
396 let simple_param = ParamValue::with_style(42, ParamStyle::Simple);
397 assert_eq!(simple_param.header_style(), ParamStyle::Simple);
398 }
399
400 #[test]
402 fn test_param_value_as_query_value() {
403 let string_param = ParamValue::new("test");
404 let query_value = string_param.as_query_value().unwrap();
405 assert_eq!(query_value, serde_json::Value::String("test".to_string()));
406
407 let number_param = ParamValue::new(42);
408 let query_value = number_param.as_query_value().unwrap();
409 assert_eq!(query_value, serde_json::Value::Number(42.into()));
410
411 let bool_param = ParamValue::new(true);
412 let query_value = bool_param.as_query_value().unwrap();
413 assert_eq!(query_value, serde_json::Value::Bool(true));
414
415 let array_param = ParamValue::new(vec!["a", "b", "c"]);
416 let query_value = array_param.as_query_value().unwrap();
417 let expected = serde_json::json!(["a", "b", "c"]);
418 assert_eq!(query_value, expected);
419 }
420
421 #[test]
422 fn test_param_value_as_header_value() {
423 let string_param = ParamValue::new("test");
424 let header_value = string_param.as_header_value().unwrap();
425 assert_eq!(header_value, serde_json::Value::String("test".to_string()));
426
427 let number_param = ParamValue::new(42);
428 let header_value = number_param.as_header_value().unwrap();
429 assert_eq!(header_value, serde_json::Value::Number(42.into()));
430 }
431
432 #[test]
433 fn test_param_value_resolve() {
434 let param = ParamValue::new(42);
435 let resolved = param.resolve(|value| {
436 assert_eq!(value, serde_json::Value::Number(42.into()));
438 utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default()))
439 });
440
441 assert!(resolved.is_some());
442 let resolved = resolved.unwrap();
443 assert_eq!(resolved.value, serde_json::Value::Number(42.into()));
444 assert_eq!(resolved.style, ParamStyle::Default);
445 }
446
447 #[test]
448 fn test_param_value_resolve_none_on_serialization_error() {
449 let param = ParamValue::new(42); let resolved = param.resolve(|_| {
453 utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default()))
454 });
455 assert!(resolved.is_some());
456 }
457
458 #[test]
460 fn test_resolved_param_value_json_value_to_string() {
461 assert_eq!(
462 ResolvedParamValue::json_value_to_string(&serde_json::Value::String(
463 "test".to_string()
464 ))
465 .unwrap(),
466 "test"
467 );
468 assert_eq!(
469 ResolvedParamValue::json_value_to_string(&serde_json::Value::Number(42.into()))
470 .unwrap(),
471 "42"
472 );
473 assert_eq!(
474 ResolvedParamValue::json_value_to_string(&serde_json::Value::Bool(true)).unwrap(),
475 "true"
476 );
477 assert_eq!(
478 ResolvedParamValue::json_value_to_string(&serde_json::Value::Bool(false)).unwrap(),
479 "false"
480 );
481 assert_eq!(
482 ResolvedParamValue::json_value_to_string(&serde_json::Value::Null).unwrap(),
483 ""
484 );
485
486 let array_result = ResolvedParamValue::json_value_to_string(&serde_json::json!(["a", "b"]));
488 assert!(array_result.is_err());
489
490 let object_result =
491 ResolvedParamValue::json_value_to_string(&serde_json::json!({"key": "value"}));
492 assert!(object_result.is_err());
493 }
494
495 #[test]
496 fn test_resolved_param_value_array_to_string_values() {
497 let arr = vec![
498 serde_json::Value::String("a".to_string()),
499 serde_json::Value::Number(42.into()),
500 serde_json::Value::Bool(true),
501 ];
502 let result = ResolvedParamValue::array_to_string_values(&arr).unwrap();
503 assert_eq!(result, vec!["a", "42", "true"]);
504
505 let nested_arr = vec![
507 serde_json::Value::String("a".to_string()),
508 serde_json::json!(["nested"]),
509 ];
510 let result = ResolvedParamValue::array_to_string_values(&nested_arr);
511 assert!(result.is_err());
512 }
513
514 #[test]
516 fn test_resolved_param_value_to_string_value_simple_values() {
517 let resolved = ResolvedParamValue {
518 value: serde_json::Value::String("test".to_string()),
519 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
520 style: ParamStyle::Default,
521 };
522 assert_eq!(resolved.to_string_value().unwrap(), "test");
523
524 let resolved = ResolvedParamValue {
525 value: serde_json::Value::Number(42.into()),
526 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
527 style: ParamStyle::Default,
528 };
529 assert_eq!(resolved.to_string_value().unwrap(), "42");
530 }
531
532 #[test]
533 fn test_resolved_param_value_to_string_value_arrays() {
534 let resolved = ResolvedParamValue {
536 value: serde_json::json!(["a", "b", "c"]),
537 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
538 style: ParamStyle::Simple,
539 };
540 assert_eq!(resolved.to_string_value().unwrap(), "a,b,c");
541
542 let resolved = ResolvedParamValue {
544 value: serde_json::json!(["a", "b", "c"]),
545 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
546 style: ParamStyle::SpaceDelimited,
547 };
548 assert_eq!(resolved.to_string_value().unwrap(), "a b c");
549
550 let resolved = ResolvedParamValue {
552 value: serde_json::json!(["a", "b", "c"]),
553 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
554 style: ParamStyle::PipeDelimited,
555 };
556 assert_eq!(resolved.to_string_value().unwrap(), "a|b|c");
557
558 let resolved = ResolvedParamValue {
560 value: serde_json::json!(["a", "b", "c"]),
561 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
562 style: ParamStyle::Form,
563 };
564 assert_eq!(resolved.to_string_value().unwrap(), "a,b,c");
565
566 let resolved = ResolvedParamValue {
568 value: serde_json::json!(["a", "b", "c"]),
569 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
570 style: ParamStyle::Default,
571 };
572 assert_eq!(resolved.to_string_value().unwrap(), "a,b,c");
573 }
574
575 #[test]
576 fn test_resolved_param_value_to_string_value_object_error() {
577 let resolved = ResolvedParamValue {
578 value: serde_json::json!({"key": "value"}),
579 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
580 style: ParamStyle::Default,
581 };
582 let result = resolved.to_string_value();
583 assert!(result.is_err());
584 assert!(matches!(
585 result.unwrap_err(),
586 ApiClientError::UnsupportedParameterValue { .. }
587 ));
588 }
589
590 #[test]
591 fn test_resolved_param_value_to_query_values_simple_values() {
592 let resolved = ResolvedParamValue {
593 value: serde_json::Value::String("test".to_string()),
594 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
595 style: ParamStyle::Default,
596 };
597 assert_eq!(resolved.to_query_values().unwrap(), vec!["test"]);
598
599 let resolved = ResolvedParamValue {
600 value: serde_json::Value::Number(42.into()),
601 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
602 style: ParamStyle::Default,
603 };
604 assert_eq!(resolved.to_query_values().unwrap(), vec!["42"]);
605 }
606
607 #[test]
608 fn test_resolved_param_value_to_query_values_arrays() {
609 let resolved = ResolvedParamValue {
611 value: serde_json::json!(["a", "b", "c"]),
612 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
613 style: ParamStyle::Form,
614 };
615 assert_eq!(resolved.to_query_values().unwrap(), vec!["a", "b", "c"]);
616
617 let resolved = ResolvedParamValue {
619 value: serde_json::json!(["a", "b", "c"]),
620 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
621 style: ParamStyle::Default,
622 };
623 assert_eq!(resolved.to_query_values().unwrap(), vec!["a", "b", "c"]);
624
625 let resolved = ResolvedParamValue {
627 value: serde_json::json!(["a", "b", "c"]),
628 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
629 style: ParamStyle::Simple,
630 };
631 assert_eq!(resolved.to_query_values().unwrap(), vec!["a,b,c"]);
632
633 let resolved = ResolvedParamValue {
635 value: serde_json::json!(["a", "b", "c"]),
636 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
637 style: ParamStyle::SpaceDelimited,
638 };
639 assert_eq!(resolved.to_query_values().unwrap(), vec!["a b c"]);
640
641 let resolved = ResolvedParamValue {
643 value: serde_json::json!(["a", "b", "c"]),
644 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
645 style: ParamStyle::PipeDelimited,
646 };
647 assert_eq!(resolved.to_query_values().unwrap(), vec!["a|b|c"]);
648 }
649
650 #[test]
651 fn test_resolved_param_value_to_query_values_object_error() {
652 let resolved = ResolvedParamValue {
653 value: serde_json::json!({"key": "value"}),
654 schema: utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default())),
655 style: ParamStyle::Default,
656 };
657 let result = resolved.to_query_values();
658 assert!(result.is_err());
659 assert!(matches!(
660 result.unwrap_err(),
661 ApiClientError::UnsupportedParameterValue { .. }
662 ));
663 }
664
665 #[test]
667 fn test_param_value_to_schema_name() {
668 assert_eq!(ParamValue::<String>::name(), String::name());
669 assert_eq!(ParamValue::<i32>::name(), i32::name());
670 assert_eq!(ParamValue::<TestStruct>::name(), TestStruct::name());
671 }
672
673 #[test]
674 fn test_param_value_partial_schema() {
675 let string_schema = ParamValue::<String>::schema();
677 let expected_schema = String::schema();
678 match (string_schema, expected_schema) {
681 (utoipa::openapi::RefOr::T(_), utoipa::openapi::RefOr::T(_)) => {}
682 (utoipa::openapi::RefOr::Ref(_), utoipa::openapi::RefOr::Ref(_)) => {}
683 _ => panic!("Schema types don't match"),
684 }
685 }
686
687 #[test]
689 fn test_param_value_with_complex_types() {
690 let struct_param = ParamValue::new(TestStruct {
691 id: 123,
692 name: "test".to_string(),
693 });
694
695 let json_value = struct_param.as_query_value().unwrap();
697 let expected = serde_json::json!({"id": 123, "name": "test"});
698 assert_eq!(json_value, expected);
699
700 let resolved = struct_param.resolve(|value| {
702 assert_eq!(value, expected);
703 utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default()))
704 });
705 assert!(resolved.is_some());
706 }
707
708 #[test]
709 fn test_param_value_with_option_types() {
710 let some_param = ParamValue::new(Some(42));
711 let json_value = some_param.as_query_value().unwrap();
712 assert_eq!(json_value, serde_json::Value::Number(42.into()));
713
714 let none_param: ParamValue<Option<i32>> = ParamValue::new(None);
715 let json_value = none_param.as_query_value().unwrap();
716 assert_eq!(json_value, serde_json::Value::Null);
717 }
718
719 #[test]
720 fn test_param_value_with_mixed_array_types() {
721 let mixed_numbers = vec![1, 2, 3];
723 let param = ParamValue::with_style(mixed_numbers, ParamStyle::SpaceDelimited);
724 let json_value = param.as_query_value().unwrap();
725 assert_eq!(json_value, serde_json::json!([1, 2, 3]));
726
727 let resolved = param
728 .resolve(|_| {
729 utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default()))
730 })
731 .unwrap();
732
733 assert_eq!(resolved.to_string_value().unwrap(), "1 2 3");
734 assert_eq!(resolved.to_query_values().unwrap(), vec!["1 2 3"]);
735 }
736
737 #[test]
739 fn test_param_value_empty_array() {
740 let empty_array: Vec<String> = vec![];
741 let param = ParamValue::new(empty_array);
742 let json_value = param.as_query_value().unwrap();
743 assert_eq!(json_value, serde_json::json!([]));
744
745 let resolved = param
746 .resolve(|_| {
747 utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default()))
748 })
749 .unwrap();
750
751 assert_eq!(resolved.to_string_value().unwrap(), "");
752 assert_eq!(resolved.to_query_values().unwrap(), Vec::<String>::new());
753 }
754
755 #[test]
756 fn test_param_value_single_item_array() {
757 let single_item = vec!["only"];
758 let param = ParamValue::new(single_item);
759 let resolved = param
760 .resolve(|_| {
761 utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(Default::default()))
762 })
763 .unwrap();
764
765 assert_eq!(resolved.to_string_value().unwrap(), "only");
766 assert_eq!(resolved.to_query_values().unwrap(), vec!["only"]);
767 }
768
769 #[test]
771 fn test_parameter_value_trait_implementation() {
772 fn accepts_parameter_value<T: ParameterValue>(_value: T) {}
773
774 accepts_parameter_value("string");
776 accepts_parameter_value(42i32);
777 accepts_parameter_value(true);
778 accepts_parameter_value(vec!["a", "b"]);
779 accepts_parameter_value(TestStruct {
780 id: 1,
781 name: "test".to_string(),
782 });
783 }
784}