use super::spell::{CARRIED_BINDING, MEMBER_SEAT, associated, generics, type_path};
use super::{AssemblyPosture, CodecShape, DecodeRefusal};
use crate::bounded::Overflow;
use crate::token::{
GeneratedDelimiter, GeneratedToken, absolute_path, attribute, documentation, group,
};
const REFUSAL_SENTENCE: &str = "Why one decode of this shape's canonical bytes refused. Holding \
one is the whole of what went wrong: the arm says which read established it and, where the \
read was about one member, which member it was standing at.";
fn derive_attribute() -> Result<Vec<GeneratedToken>, Overflow> {
let named = group(
GeneratedDelimiter::Parenthesis,
vec![
GeneratedToken::word("Debug"),
GeneratedToken::alone(','),
GeneratedToken::word("Clone"),
GeneratedToken::alone(','),
GeneratedToken::word("PartialEq"),
GeneratedToken::alone(','),
GeneratedToken::word("Eq"),
],
)?;
attribute(vec![GeneratedToken::word("derive"), named])
}
fn variant(
arm: DecodeRefusal,
payload: Option<GeneratedToken>,
) -> Result<Vec<GeneratedToken>, Overflow> {
let mut tokens = documentation(arm.sentence())?;
tokens.push(GeneratedToken::word(arm.name()));
if let Some(seat) = payload {
tokens.push(seat);
}
tokens.push(GeneratedToken::alone(','));
Ok(tokens)
}
fn member_seat() -> Result<GeneratedToken, Overflow> {
group(
GeneratedDelimiter::Brace,
vec![
GeneratedToken::word(MEMBER_SEAT),
GeneratedToken::alone(':'),
GeneratedToken::alone('&'),
GeneratedToken::joint('\''),
GeneratedToken::word("static"),
GeneratedToken::word("str"),
GeneratedToken::alone(','),
],
)
}
pub(super) fn refusal_declaration(shape: &CodecShape) -> Result<Vec<GeneratedToken>, Overflow> {
let mut variants: Vec<GeneratedToken> = Vec::new();
for arm in DecodeRefusal::ALL.iter().copied() {
if arm.carries_member() {
variants.extend(variant(arm, Some(member_seat()?))?);
}
}
variants.extend(variant(DecodeRefusal::TrailingBytes, None)?);
if let AssemblyPosture::Checked { refusal } = shape.assembly().posture() {
let carried = group(GeneratedDelimiter::Parenthesis, type_path(refusal))?;
variants.extend(variant(DecodeRefusal::NotAssembled, Some(carried))?);
}
let mut tokens = documentation(REFUSAL_SENTENCE)?;
tokens.extend(derive_attribute()?);
tokens.push(GeneratedToken::word("pub"));
tokens.push(GeneratedToken::word("enum"));
tokens.push(GeneratedToken::word(shape.refusal()));
tokens.push(group(GeneratedDelimiter::Brace, variants)?);
Ok(tokens)
}
pub(super) fn refusal_conversion(shape: &CodecShape) -> Result<Vec<GeneratedToken>, Overflow> {
let AssemblyPosture::Checked { refusal } = shape.assembly().posture() else {
return Ok(Vec::new());
};
let carried = type_path(refusal);
let mut body = vec![GeneratedToken::word("Self")];
body.extend(associated(DecodeRefusal::NotAssembled.name()));
body.push(group(
GeneratedDelimiter::Parenthesis,
vec![GeneratedToken::word(CARRIED_BINDING)],
)?);
let mut parameters = vec![
GeneratedToken::word(CARRIED_BINDING),
GeneratedToken::alone(':'),
];
parameters.extend(carried.clone());
let road = vec![
GeneratedToken::word("fn"),
GeneratedToken::word("from"),
group(GeneratedDelimiter::Parenthesis, parameters)?,
GeneratedToken::joint('-'),
GeneratedToken::alone('>'),
GeneratedToken::word("Self"),
group(GeneratedDelimiter::Brace, body)?,
];
let mut tokens = vec![GeneratedToken::word("impl")];
tokens.extend(absolute_path(&["core", "convert", "From"]));
tokens.extend(generics(carried));
tokens.push(GeneratedToken::word("for"));
tokens.push(GeneratedToken::word(shape.refusal()));
tokens.push(group(GeneratedDelimiter::Brace, road)?);
Ok(tokens)
}