use crate::exit::{EXIT_NO_RESULT, EXIT_OK, EXIT_USAGE};
use crate::query;
use super::answer::{member_candidate_object, type_candidate_object};
use super::args::Pick;
pub(crate) struct SectionAnswer {
pub(crate) code: i32,
pub(crate) out: String,
pub(crate) outcome: query::Outcome,
pub(crate) count: usize,
pub(crate) next: Vec<Vec<String>>,
}
impl SectionAnswer {
pub(crate) fn new(
code: i32,
out: String,
outcome: query::Outcome,
count: usize,
next: Vec<Vec<String>>,
) -> SectionAnswer {
SectionAnswer {
code,
out,
outcome,
count,
next,
}
}
pub(crate) fn into_tuple(self) -> (i32, String, query::Outcome, usize) {
(self.code, self.out, self.outcome, self.count)
}
}
pub(crate) struct Candidates {
query: String,
rows: Vec<String>,
objects: Vec<query::json::J>,
picked: Vec<query::PickedSeed>,
}
impl Candidates {
pub(crate) fn from_members(q: &str, candidates: &[query::MemberCandidate]) -> Candidates {
Candidates {
query: q.to_string(),
rows: candidates.iter().map(query::member_candidate_row).collect(),
objects: candidates.iter().map(member_candidate_object).collect(),
picked: candidates
.iter()
.cloned()
.map(query::PickedSeed::Member)
.collect(),
}
}
pub(crate) fn from_types(q: &str, index: &query::GraphIndex, ids: &[String]) -> Candidates {
let ordered = query::type_candidates_in_refusal_order(index, ids);
Candidates {
query: q.to_string(),
rows: ordered
.iter()
.map(|id| query::type_candidate_row(id, query::candidate_def(index, id)))
.collect(),
objects: ordered
.iter()
.map(|id| type_candidate_object(index, id))
.collect(),
picked: ordered.into_iter().map(query::PickedSeed::Type).collect(),
}
}
}
pub(crate) fn narrow<F>(
candidates: Candidates,
pick: Pick,
json: bool,
usage: &'static str,
answer_for: F,
) -> Result<SectionAnswer, (i32, String)>
where
F: Fn(&query::PickedSeed) -> SectionAnswer,
{
match pick {
Pick::Nth(n) => match candidates.picked.get(n - 1) {
Some(picked) => Ok(answer_for(picked)),
None => Err((EXIT_USAGE, usage.to_string())),
},
Pick::All => Ok(all_sections(&candidates, json, &answer_for)),
}
}
fn all_sections<F>(candidates: &Candidates, json: bool, answer_for: &F) -> SectionAnswer
where
F: Fn(&query::PickedSeed) -> SectionAnswer,
{
let answers: Vec<SectionAnswer> = candidates.picked.iter().map(answer_for).collect();
let outcome = if answers.iter().any(|a| a.outcome == query::Outcome::Hit) {
query::Outcome::Hit
} else {
query::Outcome::ZeroHit
};
let code = if outcome == query::Outcome::Hit {
EXIT_OK
} else {
EXIT_NO_RESULT
};
let count = answers.iter().map(|a| a.count).sum();
let section_next: Vec<Vec<Vec<String>>> = answers.iter().map(|a| a.next.clone()).collect();
let next = query::merge_suggestions(section_next);
let out = if json {
let answers: Vec<&str> = answers.iter().map(|a| a.out.as_str()).collect();
query::json::sections::sections_to_json(
&candidates.query,
&candidates.objects,
&answers,
&next,
outcome,
)
} else {
sections_text(candidates, &answers)
};
SectionAnswer::new(code, out, outcome, count, next)
}
fn sections_text(candidates: &Candidates, answers: &[SectionAnswer]) -> String {
let n = answers.len();
answers
.iter()
.enumerate()
.map(|(i, a)| {
format!(
"candidate {} of {n}: {}\n{}",
i + 1,
candidates.rows[i],
a.out
)
})
.collect::<Vec<_>>()
.join("\n\n")
}