1use anyhow::{Result, anyhow};
15
16const RUST_SCALARS: &[&str] = &[
21 "bool", "i8", "i16", "i32", "i64", "i128", "u8", "u16", "u32", "u64", "u128", "f32", "f64",
22 "isize", "usize", "String",
23];
24
25fn is_rust_scalar(s: &str) -> bool {
26 RUST_SCALARS.contains(&s)
27}
28
29#[derive(Debug, Clone, PartialEq)]
34pub enum VariantFieldType {
35 Scalar(std::string::String),
37 Uuid,
39 DateTime,
41 EntityId,
43 EnumRef(std::string::String),
45 Option(Box<VariantFieldType>),
47 Vec(Box<VariantFieldType>),
49}
50
51#[derive(Debug, Clone, PartialEq)]
52pub enum EnumVariantKind {
53 Simple,
54 Tuple(Vec<VariantFieldType>),
55 Struct(Vec<(std::string::String, VariantFieldType)>),
56}
57
58#[derive(Debug, Clone)]
59pub struct ParsedEnumVariant {
60 pub name: std::string::String,
61 pub kind: EnumVariantKind,
62}
63
64pub fn parse_enum_variant(raw: &str) -> Result<ParsedEnumVariant> {
71 let raw = raw.trim();
72 if raw.is_empty() {
73 return Err(anyhow!("Empty enum variant"));
74 }
75
76 let name_end = raw.find(['(', '{']).unwrap_or(raw.len());
78 let name = raw[..name_end].trim().to_string();
79
80 if name.is_empty() {
81 return Err(anyhow!("Enum variant name is empty"));
82 }
83
84 let rest = raw[name_end..].trim();
85
86 if rest.is_empty() {
87 return Ok(ParsedEnumVariant {
88 name,
89 kind: EnumVariantKind::Simple,
90 });
91 }
92
93 if rest.starts_with('(') {
94 let close = find_matching(rest, '(', ')')
95 .map_err(|_| anyhow!("Unmatched '(' in variant '{}'", name))?;
96 let trailing = rest[close + 1..].trim();
97 if !trailing.is_empty() {
98 return Err(anyhow!(
99 "Unexpected characters after ')' in variant '{}': {}",
100 name,
101 trailing
102 ));
103 }
104 let inner = rest[1..close].trim();
105 if inner.is_empty() {
106 return Err(anyhow!("Empty tuple in variant '{}'", name));
107 }
108 let fields = parse_comma_separated_types(inner)?;
109 Ok(ParsedEnumVariant {
110 name,
111 kind: EnumVariantKind::Tuple(fields),
112 })
113 } else if rest.starts_with('{') {
114 let close = find_matching(rest, '{', '}')
115 .map_err(|_| anyhow!("Unmatched '{{' in variant '{}'", name))?;
116 let trailing = rest[close + 1..].trim();
117 if !trailing.is_empty() {
118 return Err(anyhow!(
119 "Unexpected characters after '}}' in variant '{}': {}",
120 name,
121 trailing
122 ));
123 }
124 let inner = rest[1..close].trim();
125 if inner.is_empty() {
126 return Err(anyhow!("Empty struct in variant '{}'", name));
127 }
128 let fields = parse_comma_separated_named_fields(inner)?;
129 Ok(ParsedEnumVariant {
130 name,
131 kind: EnumVariantKind::Struct(fields),
132 })
133 } else {
134 Err(anyhow!(
135 "Unexpected characters after variant name '{}': {}",
136 name,
137 rest
138 ))
139 }
140}
141
142fn find_matching(s: &str, open: char, close: char) -> Result<usize> {
144 let mut depth: i32 = 0;
145 let mut angle: i32 = 0;
146
147 for (i, c) in s.char_indices() {
148 if c == open && angle == 0 {
149 depth += 1;
150 } else if c == close && angle == 0 {
151 depth -= 1;
152 if depth == 0 {
153 return Ok(i);
154 }
155 } else if c == '<' {
156 angle += 1;
157 } else if c == '>' {
158 angle -= 1;
159 }
160 }
161 Err(anyhow!("Unmatched '{}'", open))
162}
163
164fn split_respecting_angles(s: &str) -> Vec<std::string::String> {
166 let mut result = Vec::new();
167 let mut current = std::string::String::new();
168 let mut angle: i32 = 0;
169
170 for c in s.chars() {
171 match c {
172 '<' => {
173 angle += 1;
174 current.push(c);
175 }
176 '>' => {
177 angle -= 1;
178 current.push(c);
179 }
180 ',' if angle == 0 => {
181 let trimmed = current.trim().to_string();
182 if !trimmed.is_empty() {
183 result.push(trimmed);
184 }
185 current.clear();
186 }
187 _ => {
188 current.push(c);
189 }
190 }
191 }
192 let trimmed = current.trim().to_string();
193 if !trimmed.is_empty() {
194 result.push(trimmed);
195 }
196 result
197}
198
199fn parse_type(s: &str) -> Result<VariantFieldType> {
201 let s = s.trim();
202 if s.is_empty() {
203 return Err(anyhow!("Empty type"));
204 }
205
206 if let Some(inner) = s.strip_prefix("Option<").and_then(|r| r.strip_suffix('>')) {
208 let inner_type = parse_type(inner)?;
209 return Ok(VariantFieldType::Option(Box::new(inner_type)));
210 }
211 if let Some(inner) = s.strip_prefix("Vec<").and_then(|r| r.strip_suffix('>')) {
213 let inner_type = parse_type(inner)?;
214 return Ok(VariantFieldType::Vec(Box::new(inner_type)));
215 }
216
217 match s {
219 "Uuid" => return Ok(VariantFieldType::Uuid),
220 "DateTime" => return Ok(VariantFieldType::DateTime),
221 "EntityId" => return Ok(VariantFieldType::EntityId),
222 _ => {}
223 }
224
225 if is_rust_scalar(s) {
227 return Ok(VariantFieldType::Scalar(s.to_string()));
228 }
229
230 if s.chars().all(|c| c.is_alphanumeric() || c == '_')
232 && s.chars().next().is_some_and(|c| c.is_alphabetic())
233 {
234 Ok(VariantFieldType::EnumRef(s.to_string()))
235 } else {
236 Err(anyhow!(
237 "Unknown type '{}': expected a Rust type (bool, i32, i64, u64, f64, String, ...), \
238 a shorthand (Uuid, DateTime, EntityId), or a PascalCase enum name",
239 s
240 ))
241 }
242}
243
244fn parse_comma_separated_types(s: &str) -> Result<Vec<VariantFieldType>> {
246 let parts = split_respecting_angles(s);
247 let mut result = Vec::new();
248 for part in &parts {
249 result.push(parse_type(part)?);
250 }
251 Ok(result)
252}
253
254fn parse_comma_separated_named_fields(
256 s: &str,
257) -> Result<Vec<(std::string::String, VariantFieldType)>> {
258 let parts = split_respecting_angles(s);
259 let mut result = Vec::new();
260 for part in &parts {
261 let colon_pos = part
262 .find(':')
263 .ok_or_else(|| anyhow!("Struct field '{}' missing ':' separator", part))?;
264 let field_name = part[..colon_pos].trim().to_string();
265 let field_type_str = part[colon_pos + 1..].trim();
266 if field_name.is_empty() {
267 return Err(anyhow!("Empty field name in struct variant"));
268 }
269 let field_type = parse_type(field_type_str)?;
270 result.push((field_name, field_type));
271 }
272 Ok(result)
273}
274
275pub fn collect_references(variant: &ParsedEnumVariant) -> Vec<std::string::String> {
281 let mut refs = Vec::new();
282 match &variant.kind {
283 EnumVariantKind::Simple => {}
284 EnumVariantKind::Tuple(fields) => {
285 for f in fields {
286 collect_type_references(f, &mut refs);
287 }
288 }
289 EnumVariantKind::Struct(fields) => {
290 for (_, f) in fields {
291 collect_type_references(f, &mut refs);
292 }
293 }
294 }
295 refs
296}
297
298fn collect_type_references(vft: &VariantFieldType, out: &mut Vec<std::string::String>) {
299 match vft {
300 VariantFieldType::EnumRef(name) => out.push(name.clone()),
301 VariantFieldType::Option(inner) | VariantFieldType::Vec(inner) => {
302 collect_type_references(inner, out);
303 }
304 _ => {}
305 }
306}
307
308pub fn type_needs_uuid(vft: &VariantFieldType) -> bool {
310 match vft {
311 VariantFieldType::Uuid => true,
312 VariantFieldType::Option(inner) | VariantFieldType::Vec(inner) => type_needs_uuid(inner),
313 _ => false,
314 }
315}
316
317pub fn type_needs_chrono(vft: &VariantFieldType) -> bool {
319 match vft {
320 VariantFieldType::DateTime => true,
321 VariantFieldType::Option(inner) | VariantFieldType::Vec(inner) => type_needs_chrono(inner),
322 _ => false,
323 }
324}
325
326pub fn type_needs_entity_id(vft: &VariantFieldType) -> bool {
328 match vft {
329 VariantFieldType::EntityId => true,
330 VariantFieldType::Option(inner) | VariantFieldType::Vec(inner) => {
331 type_needs_entity_id(inner)
332 }
333 _ => false,
334 }
335}
336
337pub fn type_needs_float(vft: &VariantFieldType) -> bool {
339 match vft {
340 VariantFieldType::Scalar(s) => s == "f32" || s == "f64",
341 VariantFieldType::Option(inner) | VariantFieldType::Vec(inner) => type_needs_float(inner),
342 _ => false,
343 }
344}
345
346pub fn variant_needs_uuid(variant: &ParsedEnumVariant) -> bool {
348 variant_fields_iter(&variant.kind).any(type_needs_uuid)
349}
350
351pub fn variant_needs_chrono(variant: &ParsedEnumVariant) -> bool {
352 variant_fields_iter(&variant.kind).any(type_needs_chrono)
353}
354
355pub fn variant_needs_entity_id(variant: &ParsedEnumVariant) -> bool {
356 variant_fields_iter(&variant.kind).any(type_needs_entity_id)
357}
358
359pub fn variant_needs_float(variant: &ParsedEnumVariant) -> bool {
360 variant_fields_iter(&variant.kind).any(type_needs_float)
361}
362
363fn variant_fields_iter(kind: &EnumVariantKind) -> Box<dyn Iterator<Item = &VariantFieldType> + '_> {
364 match kind {
365 EnumVariantKind::Simple => Box::new(std::iter::empty()),
366 EnumVariantKind::Tuple(fields) => Box::new(fields.iter()),
367 EnumVariantKind::Struct(fields) => Box::new(fields.iter().map(|(_, f)| f)),
368 }
369}
370
371pub fn type_to_rust(vft: &VariantFieldType) -> std::string::String {
378 match vft {
379 VariantFieldType::Scalar(s) => s.clone(),
380 VariantFieldType::Uuid => "uuid::Uuid".to_string(),
381 VariantFieldType::DateTime => "chrono::DateTime<chrono::Utc>".to_string(),
382 VariantFieldType::EntityId => "EntityId".to_string(),
383 VariantFieldType::EnumRef(name) => name.clone(),
384 VariantFieldType::Option(inner) => {
385 format!("Option<{}>", type_to_rust(inner))
386 }
387 VariantFieldType::Vec(inner) => {
388 format!("Vec<{}>", type_to_rust(inner))
389 }
390 }
391}
392
393pub fn variant_to_rust_line(variant: &ParsedEnumVariant) -> std::string::String {
396 match &variant.kind {
397 EnumVariantKind::Simple => variant.name.clone(),
398 EnumVariantKind::Tuple(fields) => {
399 let types: Vec<std::string::String> = fields.iter().map(type_to_rust).collect();
400 format!("{}({})", variant.name, types.join(", "))
401 }
402 EnumVariantKind::Struct(fields) => {
403 let field_strs: Vec<std::string::String> = fields
404 .iter()
405 .map(|(name, typ)| format!("{}: {}", name, type_to_rust(typ)))
406 .collect();
407 format!("{} {{ {} }}", variant.name, field_strs.join(", "))
408 }
409 }
410}
411
412pub fn type_to_mobile_rust(vft: &VariantFieldType) -> std::string::String {
419 match vft {
420 VariantFieldType::Scalar(s) => s.clone(),
421 VariantFieldType::Uuid => "String".to_string(),
422 VariantFieldType::DateTime => "MobileDateTime".to_string(),
423 VariantFieldType::EntityId => "u64".to_string(),
424 VariantFieldType::EnumRef(name) => format!("Mobile{}", name),
425 VariantFieldType::Option(inner) => {
426 format!("Option<{}>", type_to_mobile_rust(inner))
427 }
428 VariantFieldType::Vec(inner) => {
429 format!("Vec<{}>", type_to_mobile_rust(inner))
430 }
431 }
432}
433
434pub fn variant_to_mobile_line(variant: &ParsedEnumVariant) -> std::string::String {
436 match &variant.kind {
437 EnumVariantKind::Simple => variant.name.clone(),
438 EnumVariantKind::Tuple(fields) => {
439 let types: Vec<std::string::String> = fields.iter().map(type_to_mobile_rust).collect();
440 format!("{}({})", variant.name, types.join(", "))
441 }
442 EnumVariantKind::Struct(fields) => {
443 let field_strs: Vec<std::string::String> = fields
444 .iter()
445 .map(|(name, typ)| format!("{}: {}", name, type_to_mobile_rust(typ)))
446 .collect();
447 format!("{} {{ {} }}", variant.name, field_strs.join(", "))
448 }
449 }
450}
451
452pub fn variant_match_pattern(variant: &ParsedEnumVariant) -> std::string::String {
459 match &variant.kind {
460 EnumVariantKind::Simple => variant.name.clone(),
461 EnumVariantKind::Tuple(fields) => {
462 let vars: Vec<std::string::String> =
463 (0..fields.len()).map(|i| format!("v{}", i)).collect();
464 format!("{}({})", variant.name, vars.join(", "))
465 }
466 EnumVariantKind::Struct(fields) => {
467 let names: Vec<&str> = fields.iter().map(|(n, _)| n.as_str()).collect();
468 format!("{} {{ {} }}", variant.name, names.join(", "))
469 }
470 }
471}
472
473pub fn variant_mobile_to_core_construct(variant: &ParsedEnumVariant) -> std::string::String {
476 match &variant.kind {
477 EnumVariantKind::Simple => variant.name.clone(),
478 EnumVariantKind::Tuple(fields) => {
479 let args: Vec<std::string::String> = fields
480 .iter()
481 .enumerate()
482 .map(|(i, f)| mobile_to_core_expr(&format!("v{}", i), f))
483 .collect();
484 format!("{}({})", variant.name, args.join(", "))
485 }
486 EnumVariantKind::Struct(fields) => {
487 let args: Vec<std::string::String> = fields
488 .iter()
489 .map(|(name, f)| {
490 let expr = mobile_to_core_expr(name, f);
491 if expr == *name {
492 name.clone()
493 } else {
494 format!("{}: {}", name, expr)
495 }
496 })
497 .collect();
498 format!("{} {{ {} }}", variant.name, args.join(", "))
499 }
500 }
501}
502
503pub fn variant_core_to_mobile_construct(variant: &ParsedEnumVariant) -> std::string::String {
505 match &variant.kind {
506 EnumVariantKind::Simple => variant.name.clone(),
507 EnumVariantKind::Tuple(fields) => {
508 let args: Vec<std::string::String> = fields
509 .iter()
510 .enumerate()
511 .map(|(i, f)| core_to_mobile_expr(&format!("v{}", i), f))
512 .collect();
513 format!("{}({})", variant.name, args.join(", "))
514 }
515 EnumVariantKind::Struct(fields) => {
516 let args: Vec<std::string::String> = fields
517 .iter()
518 .map(|(name, f)| {
519 let expr = core_to_mobile_expr(name, f);
520 if expr == *name {
521 name.clone()
522 } else {
523 format!("{}: {}", name, expr)
524 }
525 })
526 .collect();
527 format!("{} {{ {} }}", variant.name, args.join(", "))
528 }
529 }
530}
531
532fn mobile_to_core_expr(var: &str, vft: &VariantFieldType) -> std::string::String {
534 match vft {
535 VariantFieldType::Uuid => {
536 format!("uuid::Uuid::parse_str(&{}).unwrap_or_default()", var)
537 }
538 VariantFieldType::DateTime => format!("{}.0", var),
539 VariantFieldType::EnumRef(_) => {
540 format!("{}.into()", var)
541 }
542 VariantFieldType::Option(inner) => {
543 let inner_expr = mobile_to_core_expr("x", inner);
544 if inner_expr == "x" {
545 var.to_string()
546 } else {
547 format!("{}.map(|x| {})", var, inner_expr)
548 }
549 }
550 VariantFieldType::Vec(inner) => {
551 let inner_expr = mobile_to_core_expr("x", inner);
552 if inner_expr == "x" {
553 var.to_string()
554 } else {
555 format!("{}.into_iter().map(|x| {}).collect()", var, inner_expr)
556 }
557 }
558 _ => var.to_string(), }
560}
561
562fn core_to_mobile_expr(var: &str, vft: &VariantFieldType) -> std::string::String {
564 match vft {
565 VariantFieldType::Uuid => format!("{}.to_string()", var),
566 VariantFieldType::DateTime => format!("MobileDateTime({})", var),
567 VariantFieldType::EnumRef(_) => {
568 format!("{}.into()", var)
569 }
570 VariantFieldType::Option(inner) => {
571 let inner_expr = core_to_mobile_expr("x", inner);
572 if inner_expr == "x" {
573 var.to_string()
574 } else {
575 format!("{}.map(|x| {})", var, inner_expr)
576 }
577 }
578 VariantFieldType::Vec(inner) => {
579 let inner_expr = core_to_mobile_expr("x", inner);
580 if inner_expr == "x" {
581 var.to_string()
582 } else {
583 format!("{}.into_iter().map(|x| {}).collect()", var, inner_expr)
584 }
585 }
586 _ => var.to_string(),
587 }
588}
589
590#[cfg(test)]
595mod tests {
596 use super::*;
597
598 #[test]
599 fn test_parse_simple_variant() {
600 let v = parse_enum_variant("Active").unwrap();
601 assert_eq!(v.name, "Active");
602 assert_eq!(v.kind, EnumVariantKind::Simple);
603 }
604
605 #[test]
606 fn test_parse_tuple_variant_single() {
607 let v = parse_enum_variant("Text(String)").unwrap();
608 assert_eq!(v.name, "Text");
609 assert_eq!(
610 v.kind,
611 EnumVariantKind::Tuple(vec![VariantFieldType::Scalar("String".to_string())])
612 );
613 }
614
615 #[test]
616 fn test_parse_tuple_variant_multi() {
617 let v = parse_enum_variant("Pair(i64, String)").unwrap();
618 assert_eq!(v.name, "Pair");
619 assert_eq!(
620 v.kind,
621 EnumVariantKind::Tuple(vec![
622 VariantFieldType::Scalar("i64".to_string()),
623 VariantFieldType::Scalar("String".to_string()),
624 ])
625 );
626 }
627
628 #[test]
629 fn test_parse_struct_variant() {
630 let v = parse_enum_variant("Image { name: String, width: i64 }").unwrap();
631 assert_eq!(v.name, "Image");
632 assert_eq!(
633 v.kind,
634 EnumVariantKind::Struct(vec![
635 (
636 "name".to_string(),
637 VariantFieldType::Scalar("String".to_string())
638 ),
639 (
640 "width".to_string(),
641 VariantFieldType::Scalar("i64".to_string())
642 ),
643 ])
644 );
645 }
646
647 #[test]
648 fn test_parse_option_type() {
649 let v = parse_enum_variant("Note(Option<String>)").unwrap();
650 assert_eq!(
651 v.kind,
652 EnumVariantKind::Tuple(vec![VariantFieldType::Option(Box::new(
653 VariantFieldType::Scalar("String".to_string())
654 ))])
655 );
656 }
657
658 #[test]
659 fn test_parse_vec_type() {
660 let v = parse_enum_variant("Items(Vec<i32>)").unwrap();
661 assert_eq!(
662 v.kind,
663 EnumVariantKind::Tuple(vec![VariantFieldType::Vec(Box::new(
664 VariantFieldType::Scalar("i32".to_string())
665 ))])
666 );
667 }
668
669 #[test]
670 fn test_parse_option_vec() {
671 let v = parse_enum_variant("Data(Option<Vec<String>>)").unwrap();
672 assert_eq!(
673 v.kind,
674 EnumVariantKind::Tuple(vec![VariantFieldType::Option(Box::new(
675 VariantFieldType::Vec(Box::new(VariantFieldType::Scalar("String".to_string())))
676 ))])
677 );
678 }
679
680 #[test]
681 fn test_parse_shorthands() {
682 let v = parse_enum_variant("Stamped(Uuid, DateTime, EntityId)").unwrap();
683 assert_eq!(
684 v.kind,
685 EnumVariantKind::Tuple(vec![
686 VariantFieldType::Uuid,
687 VariantFieldType::DateTime,
688 VariantFieldType::EntityId,
689 ])
690 );
691 }
692
693 #[test]
694 fn test_parse_enum_reference() {
695 let v = parse_enum_variant("Tagged(ProjectStatus)").unwrap();
696 assert_eq!(
697 v.kind,
698 EnumVariantKind::Tuple(vec![VariantFieldType::EnumRef("ProjectStatus".to_string())])
699 );
700 }
701
702 #[test]
703 fn test_unmatched_paren() {
704 assert!(parse_enum_variant("Bad(String").is_err());
705 }
706
707 #[test]
708 fn test_unmatched_brace() {
709 assert!(parse_enum_variant("Bad { name: String").is_err());
710 }
711
712 #[test]
713 fn test_empty_tuple() {
714 assert!(parse_enum_variant("Bad()").is_err());
715 }
716
717 #[test]
718 fn test_empty_struct() {
719 assert!(parse_enum_variant("Bad {}").is_err());
720 }
721
722 #[test]
723 fn test_missing_colon_in_struct() {
724 assert!(parse_enum_variant("Bad { name String }").is_err());
725 }
726
727 #[test]
728 fn test_unknown_type_rejected() {
729 assert!(parse_enum_variant("Bad(foo::bar)").is_err());
731 }
732
733 #[test]
734 fn test_variant_to_rust_line_simple() {
735 let v = parse_enum_variant("Active").unwrap();
736 assert_eq!(variant_to_rust_line(&v), "Active");
737 }
738
739 #[test]
740 fn test_variant_to_rust_line_tuple() {
741 let v = parse_enum_variant("Text(i64)").unwrap();
742 assert_eq!(variant_to_rust_line(&v), "Text(i64)");
743 }
744
745 #[test]
746 fn test_variant_to_rust_shorthand_expansion() {
747 let v = parse_enum_variant("Stamped(Uuid, DateTime)").unwrap();
748 assert_eq!(
749 variant_to_rust_line(&v),
750 "Stamped(uuid::Uuid, chrono::DateTime<chrono::Utc>)"
751 );
752 }
753
754 #[test]
755 fn test_variant_to_rust_line_struct() {
756 let v = parse_enum_variant("Image { name: String, width: i64 }").unwrap();
757 assert_eq!(
758 variant_to_rust_line(&v),
759 "Image { name: String, width: i64 }"
760 );
761 }
762
763 #[test]
764 fn test_variant_to_rust_enum_ref() {
765 let v = parse_enum_variant("Tagged(ProjectStatus)").unwrap();
766 assert_eq!(variant_to_rust_line(&v), "Tagged(ProjectStatus)");
767 }
768
769 #[test]
770 fn test_match_pattern() {
771 let v = parse_enum_variant("Image { name: String, width: i64 }").unwrap();
772 assert_eq!(variant_match_pattern(&v), "Image { name, width }");
773
774 let v2 = parse_enum_variant("Text(String, i64)").unwrap();
775 assert_eq!(variant_match_pattern(&v2), "Text(v0, v1)");
776
777 let v3 = parse_enum_variant("Active").unwrap();
778 assert_eq!(variant_match_pattern(&v3), "Active");
779 }
780
781 #[test]
782 fn test_collect_references() {
783 let v = parse_enum_variant("Mixed(ProjectStatus, Option<TaskDifficulty>)").unwrap();
784 let refs = collect_references(&v);
785 assert_eq!(refs, vec!["ProjectStatus", "TaskDifficulty"]);
786 }
787
788 #[test]
789 fn test_variant_needs_flags() {
790 let v = parse_enum_variant("Data(Uuid, DateTime)").unwrap();
791 assert!(variant_needs_uuid(&v));
792 assert!(variant_needs_chrono(&v));
793
794 let v2 = parse_enum_variant("Simple(String)").unwrap();
795 assert!(!variant_needs_uuid(&v2));
796 assert!(!variant_needs_chrono(&v2));
797
798 let v3 = parse_enum_variant("HasId(EntityId)").unwrap();
799 assert!(variant_needs_entity_id(&v3));
800 }
801}