Skip to main content

OpError

Struct OpError 

Source
pub struct OpError(pub String);

Tuple Fields§

§0: String

Implementations§

Source§

impl OpError

Source

pub fn new(msg: impl Into<String>) -> Self

Source

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, &section_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§

Source§

impl Clone for OpError

Source§

fn clone(&self) -> OpError

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for OpError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for OpError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for OpError

Source§

impl Error for OpError

1.30.0 · Source§

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

👎Deprecated since 1.42.0:

use the Display impl or to_string()

1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0:

replaced by Error::source, which can support downcasting

Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for OpError

Source§

fn eq(&self, other: &OpError) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for OpError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.