num2phrase 0.1.0

Convert long number to a sequence of memorisable phrase with combination of short numbers
Documentation
use syn::{parse::{Parse, ParseStream, Result}, Token, spanned::Spanned};
use crate::{syntax::*, token::*};
use crate::syntax::{BIP39, DIGITS};


impl Parse for RepeatKind {
    fn parse(content: ParseStream) -> Result<Self> {
        if content.peek(syn::LitInt) {
            let lit = content.parse::<syn::LitInt>()?;
            let left = lit.base10_parse::<usize>()?;
            if content.peek(Token![,]) {
                content.parse::<Token![,]>()?;
                if content.peek(syn::LitInt) {
                    let right_lit = content.parse::<syn::LitInt>()?;
                    let right = right_lit.base10_parse::<usize>()?;
                    return Ok(RepeatKind::Between(Some(left), Some(right)));
                } else {
                    return Ok(RepeatKind::Between(Some(left), None));
                }
            } else {
                return Ok(RepeatKind::Between(Some(left), Some(left)));
            }
        }
        if content.peek(Token![,]) {
            content.parse::<Token![,]>()?;
            if content.peek(syn::LitInt) {
                let right_lit = content.parse::<syn::LitInt>()?;
                let right = right_lit.base10_parse::<usize>()?;
                Ok(RepeatKind::Between(None, Some(right)))
            } else {
                Err(syn::Error::new(content.span(), "Expected integer after comma as repeated group"))
            }
        }
        else if content.peek(MUL0) {
            content.parse::<MUL0>()?;
            Ok(RepeatKind::Between(None, None))
        } else if content.peek(MUL1) {
            content.parse::<MUL1>()?;
            Ok(RepeatKind::Between(Some(1), None))
        } else if content.peek(QST) {
            content.parse::<QST>()?;
            Ok(RepeatKind::Between(Some(0), Some(1)))
        }
        else {
            Err(syn::Error::new(content.span(), "Expected integer or comma as repeated group"))
        }
    }
}

impl Parse for CustomToken {
    fn parse(content: ParseStream) -> Result<Self> {
        if let Ok(_) = content.parse::<BIP39>() {
            Ok(CustomToken::BIP39)
        } else if let Ok(_) = content.parse::<DIGITS>() {
            Ok(CustomToken::DIGITS)
        } else {
            Err(syn::Error::new(content.span(), "Expected custom token"))
        }
    }
}

impl Parse for CurlyWrapped {
    fn parse(content: ParseStream) -> Result<Self> {
        let data;
        let brace_token = syn::braced!(data in content);
        let span = brace_token.span.span();
        if data.peek(Token![,]) || data.peek(syn::LitInt) {
            let repeat = data.parse::<RepeatKind>()?;
            Ok(CurlyWrapped::Repeat(span, repeat))
        } else {
            let token = data.parse::<CustomToken>()?;
            Ok(CurlyWrapped::Custom(span, token))
        }
    }
}

impl Parse for StructureToken {
    fn parse(input: ParseStream) -> Result<Self> {
        if input.peek(syn::Ident) {
            let ident = input.parse::<syn::Ident>()?;
            let span = ident.span();
            Ok(StructureToken::Name(span, ident.to_string()))
        } else if input.peek(syn::token::Paren) {
            let content;
            let _paren_token = syn::parenthesized!(content in input);
            let group = content.parse::<FullStructure>()?;
            Ok(StructureToken::Group(group))
        } else if input.peek(syn::token::Brace) {
            let curly = input.parse::<CurlyWrapped>()?;
            let span = match &curly {
                CurlyWrapped::Repeat(span, _) => *span,
                CurlyWrapped::Custom(span, _) => *span,
            };
            Ok(StructureToken::Curly(span, curly))
        } else if input.peek(Token![,]) || input.peek(MUL0) || input.peek(MUL1) || input.peek(QST) {
            let repeat = input.parse::<RepeatKind>()?;
            let span = input.span();
            Ok(StructureToken::Repeat(span, repeat))
        } else {
            Err(syn::Error::new(input.span(), "Expected name, group, curly or repeat"))
        }
    }
}

impl Parse for FullStructure {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut tokens = Vec::new();
        while !input.is_empty() {
            tokens.push(input.parse::<StructureToken>()?);
        }
        Ok(FullStructure { tokens })
    }
}

pub fn parse(input: &str) -> Result<FullStructure> {
    syn::parse_str::<FullStructure>(input)
}

pub fn translate(structure: &FullStructure) -> Result<IntermediateFullStructure> {
    let mut out: Vec<IntermediateToken> = vec![];
    for token in &structure.tokens {
        match token {
            StructureToken::Name(_, name) => out.push(IntermediateToken::Literal(name.clone())),
            StructureToken::Group(group) => {
                let group_tokens = translate(group)?;
                out.push(IntermediateToken::Group(group_tokens));
            },
            StructureToken::Curly(_, curly) => match curly {
                CurlyWrapped::Repeat(_, repeat) => {
                    if let Some(last) = out.pop() {
                        out.push(IntermediateToken::Repeat(Box::new(last), (*repeat).clone()));
                    } else {
                        // Error: Repeat without preceding token
                    }
                },
                CurlyWrapped::Custom(_, token) => out.push(IntermediateToken::Custom((*token).clone())),
            },
            StructureToken::Repeat(_, repeat) => {
                if let Some(last) = out.pop() {
                    out.push(IntermediateToken::Repeat(Box::new(last), (*repeat).clone()));
                } else {
                    // Error: Repeat without preceding token
                }
            },
        }
    }
    Ok(IntermediateFullStructure { tokens: out })
}