ferogram-tl-parser 0.6.5

Parser for Telegram's Type Language (.tl) schema files
Documentation
/*
 * Copyright (c) 2026 Ankit Chaubey <ankitchaubey.dev@gmail.com>
 * https://github.com/ankit-chaubey
 *
 * Project: ferogram
 * Website: https://ferogram.dev
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
 * This file may not be copied, modified, or distributed except according
 * to those terms.
 */

use std::fmt;
use std::num::ParseIntError;

/// Errors produced while parsing a single parameter token.
#[derive(Clone, Debug, PartialEq)]
pub enum ParamParseError {
    /// An empty string was encountered where a name/type was expected.
    Empty,
    /// A `{X:Type}` generic type definition (not a real error; used as a signal).
    TypeDef {
        /// The name of the generic type parameter (e.g. `"X"` from `{X:Type}`).
        name: String,
    },
    /// A `{...}` block that isn't a valid type definition.
    MissingDef,
    /// A flag expression (`name.N?Type`) was malformed.
    InvalidFlag,
    /// A generic `<...>` argument was malformed (missing closing `>`).
    InvalidGeneric,
    /// A bare `name` with no `:type`: e.g. old-style `? = Int`.
    NotImplemented,
}

impl fmt::Display for ParamParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Empty => write!(f, "empty token"),
            Self::TypeDef { name } => write!(f, "generic type definition: {name}"),
            Self::MissingDef => write!(f, "unknown generic or flag definition"),
            Self::InvalidFlag => write!(f, "invalid flag expression"),
            Self::InvalidGeneric => write!(f, "invalid generic argument (unclosed `<`)"),
            Self::NotImplemented => write!(f, "parameter without `:type` is not supported"),
        }
    }
}

impl std::error::Error for ParamParseError {}

/// Errors produced while parsing a complete TL definition.
#[derive(Debug, PartialEq)]
pub enum ParseError {
    /// The input was blank.
    Empty,
    /// No `= Type` was found.
    MissingType,
    /// The name (before `=`) was missing or had empty namespace components.
    MissingName,
    /// The `#id` hex literal was unparseable.
    InvalidId(ParseIntError),
    /// A parameter was invalid.
    InvalidParam(ParamParseError),
    /// The definition uses a syntax we don't support yet.
    NotImplemented,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Empty => write!(f, "empty definition"),
            Self::MissingType => write!(f, "missing `= Type`"),
            Self::MissingName => write!(f, "missing or malformed name"),
            Self::InvalidId(e) => write!(f, "invalid constructor ID: {e}"),
            Self::InvalidParam(e) => write!(f, "invalid parameter: {e}"),
            Self::NotImplemented => write!(f, "unsupported TL syntax"),
        }
    }
}

impl std::error::Error for ParseError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::InvalidId(e) => Some(e),
            Self::InvalidParam(e) => Some(e),
            _ => None,
        }
    }
}