cmakefmt-rust 1.4.2

A fast, correct CMake formatter
Documentation
// SPDX-FileCopyrightText: Copyright 2026 Puneet Matharu
//
// SPDX-License-Identifier: MIT OR Apache-2.0

//! AST types returned by [`crate::parser::parse`].
//!
//! A CMake file parses into a [`File`] containing an ordered list of
//! top-level [`Statement`]s. Commands carry their argument list
//! ([`Argument`]), recognised comment forms ([`Comment`]), and the
//! source byte span. The AST preserves blank-line and comment
//! positions so the formatter can round-trip files with stable
//! semantics.

/// A parsed CMake source file.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct File {
    /// Top-level statements in source order.
    pub statements: Vec<Statement>,
}

/// A top-level statement in a CMake file.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Statement {
    /// A command invocation, e.g. `target_link_libraries(foo PUBLIC bar)`.
    Command(CommandInvocation),
    /// A top-level configure-file placeholder line such as `@PACKAGE_INIT@`.
    ///
    /// These occur in `.cmake.in` templates and must be preserved verbatim.
    TemplatePlaceholder(String),
    /// A standalone comment (on its own line).
    Comment(Comment),
    /// One or more consecutive blank lines between statements.
    /// The value is the number of blank lines (>= 1).
    ///
    /// Blank lines at the start of the file and at the end of the
    /// file are also preserved as `BlankLines` statements, so a
    /// round-tripped AST matches the source's whitespace envelope.
    BlankLines(usize),
}

/// A CMake command invocation.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommandInvocation {
    /// The command name, e.g. "target_link_libraries". Case as written in source.
    pub name: String,
    /// The argument list, in source order.
    pub arguments: Vec<Argument>,
    /// A comment that appears after the closing paren on the same line.
    pub trailing_comment: Option<Comment>,
    /// Half-open byte range `[start, end)` into the original source.
    /// `start` is inclusive, `end` is exclusive.
    pub span: (usize, usize),
}

/// A single argument (or inline comment) in an argument list.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Argument {
    /// `[[...]]`, `[=[...]=]`, etc. Content is verbatim.
    Bracket(BracketArgument),
    /// `"..."` — includes the surrounding quotes verbatim.
    Quoted(String),
    /// Any other token — unquoted argument, variable reference
    /// (`${VAR}`), environment reference (`$ENV{X}`), cache reference
    /// (`$CACHE{X}`), generator expression (`$<...>`), legacy
    /// unquoted arguments containing embedded `"..."` segments, or a
    /// parenthesised group inside a condition (e.g. `(A OR B)`
    /// inside `if(...)`).
    Unquoted(String),
    /// A comment that appears inline between arguments.
    InlineComment(Comment),
}

impl Argument {
    /// The source text of this argument. For
    /// [`Argument::InlineComment`] the returned slice includes the
    /// leading `#` (and, for bracket comments, the enclosing
    /// `#[[...]]` delimiters).
    pub fn as_str(&self) -> &str {
        match self {
            Argument::Bracket(b) => &b.raw,
            Argument::Quoted(s) | Argument::Unquoted(s) => s,
            Argument::InlineComment(c) => c.as_str(),
        }
    }

    /// Returns `true` when the argument is an inline comment placeholder rather
    /// than a normal CMake argument token.
    pub fn is_comment(&self) -> bool {
        matches!(self, Argument::InlineComment(_))
    }
}

/// A bracket argument with its "=" nesting level.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BracketArgument {
    /// Number of `=` characters between the outer brackets. 0 = `[[...]]`.
    pub level: usize,
    /// The raw source text, e.g. `[==[content]==]`.
    pub raw: String,
}

/// A CMake comment.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Comment {
    /// `# text to end of line` (stored with the leading `#`).
    Line(String),
    /// `#[[...]]` or `#[=[...]=]` (stored as the full raw text including `#`).
    Bracket(String),
}

impl Comment {
    /// Return the raw source text of the comment, including the leading `#`.
    pub fn as_str(&self) -> &str {
        match self {
            Comment::Line(s) | Comment::Bracket(s) => s,
        }
    }
}