schachmatt 0.3.0

A chess library
Documentation
use crate::{Field, PieceType, Turn};

use pest::{Parser, iterators::Pair};

use super::Lan;

#[derive(Parser)]
#[grammar = "parser/long_algebraic_notation/long_algebraic_notation.pest"]
struct LanStruct;

impl Lan {
    /// Converts a string in LAN into a `Turn` if possible.
    /// - `raw` - The raw string in long algebraic notation
    /// - `color_at_turn` - The color who played the given turn
    /// - `returns` - A turn representing the raw data or none
    #[must_use]
    pub fn import(raw: &str) -> Option<Turn> {
        let Ok(mut parsed_data) = LanStruct::parse(Rule::turn, raw) else {
            return None;
        };

        if let Some(turn_type) = parsed_data.next().unwrap().into_inner().next() {
            match turn_type.as_rule() {
                Rule::from_to_turn => {
                    let (from, to) = Self::handle_from_to_turn_rule(turn_type);
                    return Some(Turn {
                        current: from,
                        target: to,
                        promotion: None,
                    });
                }
                Rule::piece_descriptor_turn => {
                    let (from, to) = Self::handle_piece_descriptor_turn_rule(turn_type);
                    return Some(Turn {
                        current: from,
                        target: to,
                        promotion: None,
                    });
                }
                Rule::promotion_turn => {
                    return Some(Self::handle_promotion_turn_rule(turn_type));
                }
                _ => unreachable!(),
            }
        }
        unreachable!()
    }

    /// Converts the `promotion-turn`-Rule into a `Turn`-object
    /// - `promotion_turn` - The matching Rule
    /// - `returns` - A `Turn`-object
    fn handle_promotion_turn_rule(promotion_turn: Pair<Rule>) -> Turn {
        let mut from_field: Option<Field> = None;
        let mut to_field: Option<Field> = None;

        for promotion_turn_descriptor in promotion_turn.into_inner() {
            match promotion_turn_descriptor.as_rule() {
                Rule::from_to_turn => {
                    let (temp_from_field, temp_to_field) =
                        Self::handle_from_to_turn_rule(promotion_turn_descriptor);
                    from_field = Some(temp_from_field);
                    to_field = Some(temp_to_field);
                }
                Rule::piece_descriptor_turn => {
                    let (temp_from_field, temp_to_field) =
                        Self::handle_piece_descriptor_turn_rule(promotion_turn_descriptor);
                    from_field = Some(temp_from_field);
                    to_field = Some(temp_to_field);
                }
                Rule::promotion_piece => {
                    let piece_representation = promotion_turn_descriptor.as_str().as_bytes()[0];
                    let piece_type =
                        PieceType::import_piecetype(piece_representation as char).unwrap();

                    return Turn {
                        current: from_field.unwrap(),
                        target: to_field.unwrap(),
                        promotion: Some(piece_type),
                    };
                }
                _ => unreachable!(),
            }
        }
        unreachable!()
    }

    /// Converts the `piece-descriptor-turn`-Rule into two fields
    /// - `piece_descriptor_turn` - The matching Rule
    /// - `returns` - The two fields (`from_field`, `to_field`)
    fn handle_piece_descriptor_turn_rule(piece_descriptor_turn: Pair<Rule>) -> (Field, Field) {
        for piece_descriptor in piece_descriptor_turn.into_inner() {
            if piece_descriptor.as_rule() == Rule::from_to_turn {
                return Self::handle_from_to_turn_rule(piece_descriptor);
            }
        }
        unreachable!()
    }

    /// Converts the `from-to-turn`-Rule into two fields
    /// - `from_to_turn` - The matching Rule
    /// - `returns` - The two fields (`from_field`, `to_field`)
    fn handle_from_to_turn_rule(from_to_turn: Pair<Rule>) -> (Field, Field) {
        let mut from_field: Option<Field> = None;

        for field_descriptor in from_to_turn.into_inner() {
            if field_descriptor.as_rule() == Rule::field_descriptor {
                let field = Field::new_from_string(field_descriptor.as_str()).unwrap();
                if let Some(from) = from_field {
                    return (from, field);
                }
                from_field = Some(field);
            }
        }
        unreachable!()
    }
}