pub struct OpError(pub String);Tuple Fields§
§0: StringImplementations§
Source§impl OpError
impl OpError
pub fn new(msg: impl Into<String>) -> Self
Sourcepub fn message(&self) -> &str
pub fn message(&self) -> &str
Examples found in repository?
examples/oracle_cases.rs (line 354)
340fn run(content: &str, op: &str, a: &Args) -> (&'static str, String) {
341 match op {
342 "py_repr" => {
343 let text = a.get("text").map(String::as_str).unwrap_or("");
344 match json::parse(text) {
345 None => ("ok", "UNPARSED".to_string()),
346 Some(v) => ("ok", json::py_repr(&v)),
347 }
348 }
349 "check_args" => {
350 let f = a.get("fn").map(String::as_str).unwrap_or("");
351 let text = a.get("text").map(String::as_str).unwrap_or("");
352 match arg_case(f, text) {
353 Ok(s) => ("ok", s),
354 Err(e) => ("err", e.message().to_string()),
355 }
356 }
357 "render_table_list" => (
358 "ok",
359 render_table_list(content, a.get("path").map(String::as_str).unwrap_or("")),
360 ),
361 "list_tables" => {
362 let dump = list_tables(content)
363 .iter()
364 .map(|e| {
365 format!(
366 "{}|{}|{}|{}|{}",
367 e.ordinal,
368 e.heading,
369 e.caption,
370 e.columns.join(" | "),
371 e.rows
372 )
373 })
374 .collect::<Vec<_>>()
375 .join("\n");
376 ("ok", dump)
377 }
378 "find_tables" => {
379 let dump = find_tables(content)
380 .iter()
381 .map(|t| format!("{}|{}|{}|{}", t.start, t.end, t.is_aligned(), t.has_tabs()))
382 .collect::<Vec<_>>()
383 .join("\n");
384 ("ok", dump)
385 }
386 "find_sections" => {
387 let dump = find_sections(content)
388 .iter()
389 .map(|s| {
390 format!(
391 "{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
392 s.start,
393 s.heading_end,
394 s.own_end,
395 s.end,
396 s.level,
397 s.style.as_str(),
398 s.gap_after,
399 s.parent.map(|p| p as isize).unwrap_or(-1),
400 esc(&s.indent),
401 s.marker,
402 esc(&s.space),
403 s.closing,
404 esc(&s.raw_text),
405 s.path.join(" > ")
406 )
407 })
408 .collect::<Vec<_>>()
409 .join("\n");
410 ("ok", dump)
411 }
412 // The list parser, field by field, for the reason `find_sections` is
413 // dumped: an op suite can agree everywhere and still be sitting on a
414 // parser that disagrees about where a list ends. One line per list,
415 // then one indented line per item -- `own_end` and `parent` are in
416 // there because nothing else reads them, so nothing else would notice.
417 "find_lists" => {
418 let mut dump: Vec<String> = Vec::new();
419 for l in find_lists(content) {
420 dump.push(format!(
421 "{}|{}|{}|{}|{}|{}",
422 l.start,
423 l.end,
424 l.ordered,
425 l.bullet,
426 esc(&l.indent),
427 l.loose
428 ));
429 for it in &l.items {
430 dump.push(format!(
431 " {}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
432 it.start,
433 it.own_end,
434 it.end,
435 it.depth,
436 esc(&it.indent),
437 it.marker,
438 it.ordered,
439 opt(it.number.map(|n| n.to_string())),
440 opt(it.delim.map(|c| c.to_string())),
441 opt(it.checkbox.map(|c| c.to_string())),
442 it.parent.map(|p| p as isize).unwrap_or(-1),
443 esc(&it.text)
444 ));
445 }
446 }
447 ("ok", dump.join("\n"))
448 }
449 // The frontmatter parser, field by field, for the reason the other two
450 // are dumped -- plus `rebuilt()`, which no other dump has an analogue
451 // for. Every edit in the family goes out through it, so comparing it
452 // here catches a gap or padding divergence on a line no op in the suite
453 // happens to rewrite.
454 // `parse_path`, typed and round-tripped. Its own case op because the
455 // parser only ever hands out paths it built itself, so the refusal
456 // branch and the index arithmetic are reachable from no fixture.
457 "parse_path" => match parse_path(a.get("text").map(String::as_str).unwrap_or("")) {
458 None => ("ok", "NONE".to_string()),
459 Some(p) => {
460 let kinds = p
461 .iter()
462 .map(|s| match s {
463 Seg::Index(n) => format!("i:{n}"),
464 Seg::Key(k) => format!("k:{k}"),
465 })
466 .collect::<Vec<_>>()
467 .join("|");
468 ("ok", format!("{}\n{kinds}", format_path(&p)))
469 }
470 },
471 "find_frontmatter" => {
472 let fm = find_frontmatter(content);
473 if !fm.present {
474 return ("ok", "absent".to_string());
475 }
476 let mut dump = vec![format!(
477 "{}|{}|{}|{}|{}|{}",
478 match fm.fmt {
479 Some(Fmt::Yaml) => "yaml",
480 _ => "toml",
481 },
482 fm.start,
483 fm.end,
484 fm.delim,
485 fm.close,
486 esc(&fm.eol)
487 )];
488 for e in &fm.entries {
489 dump.push(format!(
490 " {}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
491 format_path(&e.path),
492 e.line,
493 e.end,
494 e.kind.as_str(),
495 esc(&e.prefix),
496 esc(&e.key_text),
497 esc(&e.gap),
498 esc(&e.value),
499 esc(&e.pad),
500 esc(&e.comment),
501 esc(&e.eol),
502 esc(&e.rebuilt(None))
503 ));
504 }
505 ("ok", dump.join("\n"))
506 }
507 // `describe_change`'s case, for the family it does not describe. The
508 // same shape deliberately: both sides apply the op and describe
509 // before-vs-after, so a divergence in either half shows here.
510 "describe_front" => {
511 let name = a.get("opname").map(String::as_str).unwrap_or("");
512 let arg = a.get("args").map(String::as_str).unwrap_or("ABSENT");
513 let parsed = if arg == "ABSENT" {
514 None
515 } else {
516 json::parse(arg)
517 };
518 if arg != "ABSENT" && parsed.is_none() {
519 ("ok", format!("PARSE-FAILED {arg}"))
520 } else {
521 match apply_op(content, name, parsed.as_ref()) {
522 Ok(after) => ("ok", describe_frontmatter_change(content, &after)),
523 Err(e) => ("err", e.0),
524 }
525 }
526 }
527 "render_frontmatter" => (
528 "ok",
529 render_frontmatter(content, a.get("path").map(String::as_str).unwrap_or("")),
530 ),
531 // Through the rendered form for `render_table_get`'s reason — it is
532 // what a model receives — and through the structure as well, because
533 // that is what `keys --json` carries and `grade.py` scores. `state` and
534 // `format` appear in neither rendering, so a port that got them wrong
535 // would pass on the text alone.
536 "frontmatter_get" => {
537 let path = a.get("path").map(String::as_str).unwrap_or("");
538 // Absent and an explicit `null` are the same call here, which is
539 // the asymmetry with `frontmatter-set` the oracle spells with a
540 // `key=None` default. Parsing gives `Value::Null` for the second
541 // and the op filters it, so both arrive as no key.
542 let key = match a.get("key") {
543 None => None,
544 Some(text) => match json::parse(text) {
545 Some(v) => Some(v),
546 None => return ("ok", format!("PARSE-FAILED {text}")),
547 },
548 };
549 let got = match frontmatter_get(content, key.as_ref()) {
550 Ok(g) => g,
551 Err(e) => return ("err", e.0),
552 };
553 let text = match render_frontmatter_get(content, path, key.as_ref()) {
554 Ok(t) => t,
555 Err(e) => return ("err", e.0),
556 };
557 let mut dump = vec![format!(
558 "{}|{}|{}",
559 got.state,
560 match got.format {
561 Some(Fmt::Yaml) => "yaml",
562 Some(Fmt::Toml) => "toml",
563 // Python's `None`, printed as an f-string prints it: the
564 // oracle's dict holds the parser's `fmt` unconverted, and
565 // an absent block has no format at all.
566 None => "None",
567 },
568 got.keys.len()
569 )];
570 for k in &got.keys {
571 dump.push(format!(
572 "{}|{}|{}|{}",
573 k.path,
574 k.kind,
575 esc(&k.value),
576 k.lines
577 ));
578 }
579 dump.push("--".to_string());
580 dump.push(text);
581 ("ok", dump.join("\n"))
582 }
583 // The list summary, field by field. The rendering is compared too, one
584 // case below -- it is the Arm B prompt, so a divergence in it is a
585 // divergence in what the model was measured on, not in a debug format.
586 "list_lists" => {
587 let dump = list_lists(content)
588 .iter()
589 .map(|e| {
590 format!(
591 "{}|{}|{}|{}|{}|{}|{}|{}",
592 e.ordinal, e.heading, e.kind, e.marker, e.items, e.levels, e.loose, e.tasks
593 )
594 })
595 .collect::<Vec<_>>()
596 .join("\n");
597 ("ok", dump)
598 }
599 "render_list_summary" => (
600 "ok",
601 render_list_summary(content, a.get("path").map(String::as_str).unwrap_or("")),
602 ),
603 // The section outline, field by field, and its rendering one case
604 // below. The render is the section family's prompt context, so a
605 // divergence in it is a divergence in what a model was measured on.
606 "section_outline" => {
607 let dump = section_outline(content)
608 .iter()
609 .map(|e| {
610 format!(
611 "{}|{}|{}|{}|{}|{}|{}|{}",
612 e.level,
613 e.path,
614 e.text,
615 e.style,
616 e.ordinal,
617 e.unique,
618 e.has_body,
619 e.subsections
620 )
621 })
622 .collect::<Vec<_>>()
623 .join("\n");
624 ("ok", dump)
625 }
626 "render_section_outline" => (
627 "ok",
628 render_section_outline(content, a.get("path").map(String::as_str).unwrap_or("")),
629 ),
630 // Three ends, not one: `own_end` and `end` are the field the ops
631 // disagree about, so a resolver that found the right section with the
632 // wrong subtree boundary would pass a `start`-only comparison.
633 "resolve_section" => match resolve_section(content, §ion_address(a)) {
634 Ok(s) => (
635 "ok",
636 format!(
637 "{}|{}|{}|{}|{}",
638 s.start, s.heading_end, s.own_end, s.end, s.level
639 ),
640 ),
641 Err(e) => ("err", e.0),
642 },
643 "resolve_list" => match resolve_list(content, &list_address(a)) {
644 Ok(l) => ("ok", format!("{}|{}", l.start, l.end)),
645 Err(e) => ("err", e.0),
646 },
647 "resolve_item" => match resolve_list(content, &list_address(a))
648 .and_then(|l| resolve_item(&l, a.get("item").map(String::as_str), "item"))
649 {
650 Ok(i) => ("ok", i.to_string()),
651 Err(e) => ("err", e.0),
652 },
653 "inert_headings" => {
654 let dump = inert_headings(content)
655 .iter()
656 .map(|h| format!("{}|{}|{}", h.line, h.reason, h.text))
657 .collect::<Vec<_>>()
658 .join("\n");
659 ("ok", dump)
660 }
661 "resolve_table" => match resolve_table(content, &address(a)) {
662 Ok(t) => ("ok", format!("{}|{}", t.start, t.end)),
663 Err(e) => ("err", e.0),
664 },
665 "resolve_row" => match resolve_table(content, &address(a))
666 .and_then(|t| resolve_row(&t, &where_pairs(a)))
667 {
668 Ok(i) => ("ok", i.to_string()),
669 Err(e) => ("err", e.0),
670 },
671 "add_row" => match table_add_row(content, &address(a), &values(a), position(a).as_ref()) {
672 Ok(s) => ("ok", s),
673 Err(e) => ("err", e.0),
674 },
675 "update_cell" => match table_update_cell(
676 content,
677 &address(a),
678 &where_pairs(a),
679 // Both raw, and both defaulted to "" -- the Python side passes
680 // `args.get("column", "")` and `args.get("value", "")`, so an absent
681 // argument is the empty string on this side too.
682 Some(&json::Value::Str(
683 a.get("column").cloned().unwrap_or_default(),
684 )),
685 Some(&json::Value::Str(
686 a.get("value").cloned().unwrap_or_default(),
687 )),
688 ) {
689 Ok(s) => ("ok", s),
690 Err(e) => ("err", e.0),
691 },
692 // The read op, through its rendered form -- which is the whole result:
693 // heading, matched/total counts, columns and every cell all appear in
694 // the text, so comparing the render compares the struct behind it.
695 // Comparing `TableRows` field by field would test a debug format that
696 // no caller ever sees.
697 "table_get" => {
698 // Two wire formats, one op. The flat one is what every table case
699 // uses; `args` is the typed hatch, and a case carrying it takes the
700 // address from there too, or an untyped address would silently
701 // override the typed one.
702 let (addr, filt) = match typed_args(a) {
703 Some(v) => (typed_address(v.get("table")), typed_filter(&v)),
704 None => (address(a), filter_arg(a)),
705 };
706 match render_table_get(content, &addr, filt.as_ref()) {
707 Ok(s) => ("ok", s),
708 Err(e) => ("err", e.0),
709 }
710 }
711 "delete_row" => match table_delete_row(content, &address(a), &where_pairs(a)) {
712 Ok(s) => ("ok", s),
713 Err(e) => ("err", e.0),
714 },
715 // The whole dispatch, arguments and all -- the op name and the raw
716 // argument text, exactly as `difftest.py` hands them to `F.apply_op`.
717 // What is compared is the message a model would actually receive, so
718 // the argument-checking *order* (dispatch.rs's whole reason to exist)
719 // is under test here and nowhere else.
720 "apply_op" => {
721 let name = a.get("opname").map(String::as_str).unwrap_or("");
722 let text = a.get("args").map(String::as_str).unwrap_or("ABSENT");
723 let parsed = if text == "ABSENT" {
724 None
725 } else {
726 json::parse(text)
727 };
728 // Every DISPATCH_ARGS entry but `ABSENT` is valid JSON by
729 // construction, so a `None` here is a `parse` bug. Saying so beats
730 // passing `None` on and quietly testing the absent-argument path.
731 if text != "ABSENT" && parsed.is_none() {
732 ("ok", format!("PARSE-FAILED {text}"))
733 } else {
734 match apply_op(content, name, parsed.as_ref()) {
735 Ok(s) => ("ok", s),
736 Err(e) => ("err", e.0),
737 }
738 }
739 }
740 // `describe_change` needs two documents, and a case carries one
741 // fixture. So it carries the *op* that produces the second: both sides
742 // apply it and describe before-vs-after, which keeps the wire cheap and
743 // reuses a document the corpus already vouches for. A refusal is
744 // returned as itself — those messages are compared by the `apply_op`
745 // family anyway, so nothing is lost and no case is wasted.
746 //
747 // `after` is the escape hatch for the branches no op reaches. It rides
748 // as a JSON string because a raw newline would break the line-framed
749 // case file, which is the same reason `apply_op` passes its arguments
750 // that way.
751 "describe_change" => {
752 if let Some(text) = a.get("after") {
753 return match json::parse(text) {
754 Some(json::Value::Str(s)) => ("ok", describe_change(content, &s)),
755 _ => ("ok", format!("PARSE-FAILED {text}")),
756 };
757 }
758 let name = a.get("opname").map(String::as_str).unwrap_or("");
759 let arg = a.get("args").map(String::as_str).unwrap_or("ABSENT");
760 let parsed = if arg == "ABSENT" {
761 None
762 } else {
763 json::parse(arg)
764 };
765 if arg != "ABSENT" && parsed.is_none() {
766 ("ok", format!("PARSE-FAILED {arg}"))
767 } else {
768 match apply_op(content, name, parsed.as_ref()) {
769 Ok(after) => ("ok", describe_change(content, &after)),
770 Err(e) => ("err", e.0),
771 }
772 }
773 }
774 other => ("err", format!("unknown op {other}")),
775 }
776}Trait Implementations§
impl Eq for OpError
Source§impl Error for OpError
impl Error for OpError
1.30.0 · Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
👎Deprecated since 1.42.0:
use the Display impl or to_string()
impl StructuralPartialEq for OpError
Auto Trait Implementations§
impl Freeze for OpError
impl RefUnwindSafe for OpError
impl Send for OpError
impl Sync for OpError
impl Unpin for OpError
impl UnsafeUnpin for OpError
impl UnwindSafe for OpError
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more