use serde_json::Value;
use crate::pyjson::Dumps;
use crate::request::{Criteria, Question};
const RAW: Dumps = Dumps { ensure_ascii: false };
const ASCII: Dumps = Dumps { ensure_ascii: true };
pub const NOUL_FALSE: &str = "no, the statement does not hold";
pub const NOUL_TRUE: &str = "yes, the statement holds";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompatText {
pub head: String,
pub options: Vec<String>,
}
#[must_use]
pub fn compat_question(q: &Question, mask: &str) -> CompatText {
let ins = match &q.instructions {
None => String::new(),
Some(Value::String(s)) => s.clone(),
Some(v) => ASCII.to_string(v),
};
let head = format!("{} question: {}", q.qtype.as_str(), ins.replace(mask, " "));
let options = compat_options(&q.criteria)
.into_iter()
.map(|o| format!(" {}", o.replace(mask, " ")))
.collect();
CompatText { head, options }
}
#[must_use]
pub fn compat_options(c: &Criteria) -> Vec<String> {
match c {
Criteria::Choice(opts) => opts
.iter()
.map(|o| match &o.description {
None => o.label.clone(),
Some(Value::String(s)) if s.is_empty() => o.label.clone(),
Some(v) => format!("{}: {}", o.label, criterion(v)),
})
.collect(),
Criteria::Score(levels) => {
levels.iter().enumerate().map(|(i, v)| format!("level {i}: {}", criterion(v))).collect()
}
Criteria::Noul { when_false, when_true, labels } => {
let side = |v: &Option<Value>, default: &str| match v {
None | Some(Value::Null) => default.to_string(),
Some(Value::String(s)) if s.is_empty() => default.to_string(),
Some(v) => criterion(v),
};
let (f, t) =
labels.as_ref().map_or(("false", "true"), |(f, t)| (f.as_str(), t.as_str()));
vec![
format!("{f}: {}", side(when_false, NOUL_FALSE)),
format!("{t}: {}", side(when_true, NOUL_TRUE)),
]
}
}
}
#[must_use]
pub fn criterion(v: &Value) -> String {
match v {
Value::String(s) => s.clone(),
v => RAW.to_string(v),
}
}
#[must_use]
pub fn compat_state(state: &Value, mask: &str) -> String {
let s = match state {
Value::String(s) => s.clone(),
v => RAW.to_string(v),
};
s.replace(mask, " ")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::request::{Limits, parse};
use serde_json::json;
fn render(q: Value) -> CompatText {
let r = parse(&json!({"state": "", "questions": {"q": q}}), &Limits::LAYA).unwrap();
compat_question(&r.questions[0], "[MASK]")
}
#[test]
fn choice_score_noul() {
let t = render(
json!({"type": "choice", "instructions": "Pick [MASK] one", "criteria": {"a": "", "b": null, "c": 0, "d": {"x": [1, 2.5]}}}),
);
assert_eq!(t.head, "choice question: Pick one");
assert_eq!(t.options, [" a", " b", " c: 0", " d: {\"x\": [1, 2.5]}"]);
let t = render(
json!({"type": "score", "instructions": {"ask": "é"}, "criteria": ["low", null, true]}),
);
assert_eq!(t.head, "score question: {\"ask\": \"\\u00e9\"}");
assert_eq!(t.options, [" level 0: low", " level 1: null", " level 2: true"]);
let t = render(
json!({"type": "noul", "instructions": null, "criteria": {"TRUE": "", "false": "nope"}}),
);
assert_eq!(t.head, "noul question: null");
assert_eq!(t.options, [" false: nope", " true: yes, the statement holds"]);
}
#[test]
fn state() {
assert_eq!(
compat_state(&json!({"k": "ü", "n": [1e-5]}), "[MASK]"),
"{\"k\": \"ü\", \"n\": [1e-05]}"
);
assert_eq!(compat_state(&json!("a<mask>b"), "<mask>"), "a b");
}
}