mirror of
https://github.com/zizmorcore/zizmor.git
synced 2025-12-23 08:47:33 +00:00
refactor: move expr call APIs to a new module (#1143)
This commit is contained in:
parent
5a4d4e5785
commit
4a92dfc412
14 changed files with 1266 additions and 1216 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -828,7 +828,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
|||
|
||||
[[package]]
|
||||
name = "github-actions-expressions"
|
||||
version = "0.0.9"
|
||||
version = "0.0.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ rust-version = "1.88.0"
|
|||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.99"
|
||||
github-actions-expressions = { path = "crates/github-actions-expressions", version = "0.0.9" }
|
||||
github-actions-expressions = { path = "crates/github-actions-expressions", version = "0.0.10" }
|
||||
github-actions-models = { path = "crates/github-actions-models", version = "0.32.0" }
|
||||
itertools = "0.14.0"
|
||||
pest = "2.8.1"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
name = "github-actions-expressions"
|
||||
description = "GitHub Actions expression parser and data types"
|
||||
repository = "https://github.com/zizmorcore/zizmor/tree/main/crates/github-actions-expressions"
|
||||
version = "0.0.9"
|
||||
version = "0.0.10"
|
||||
readme = "README.md"
|
||||
|
||||
homepage.workspace = true
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@
|
|||
|
||||
`github-actions-expressions` is a parser and library for GitHub Actions expressions.
|
||||
|
||||
Key features:
|
||||
|
||||
* Faithful parsing of GitHub Actions expressions.
|
||||
* Span-aware AST nodes.
|
||||
* Limited support for constant expression evaluation.
|
||||
|
||||
See the [documentation] for more details.
|
||||
|
||||
This library is part of [zizmor].
|
||||
|
|
|
|||
1050
crates/github-actions-expressions/src/call.rs
Normal file
1050
crates/github-actions-expressions/src/call.rs
Normal file
File diff suppressed because it is too large
Load diff
31
crates/github-actions-expressions/src/identifier.rs
Normal file
31
crates/github-actions-expressions/src/identifier.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
//! Identifiers.
|
||||
|
||||
/// Represents a single identifier in a GitHub Actions expression,
|
||||
/// i.e. a single context component.
|
||||
///
|
||||
/// Identifiers are case-insensitive.
|
||||
#[derive(Debug)]
|
||||
pub struct Identifier<'src>(pub(crate) &'src str);
|
||||
|
||||
impl Identifier<'_> {
|
||||
/// Returns the identifier as a string slice, as it appears in the
|
||||
/// expression.
|
||||
///
|
||||
/// Important: identifiers are case-insensitive, so this should not
|
||||
/// be used for comparisons.
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Identifier<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.eq_ignore_ascii_case(other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Identifier<'_> {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.0.eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
75
crates/github-actions-expressions/src/literal.rs
Normal file
75
crates/github-actions-expressions/src/literal.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
//! Literal values.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::Evaluation;
|
||||
|
||||
/// Represents a literal value in a GitHub Actions expression.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Literal<'src> {
|
||||
/// A number literal.
|
||||
Number(f64),
|
||||
/// A string literal.
|
||||
String(Cow<'src, str>),
|
||||
/// A boolean literal.
|
||||
Boolean(bool),
|
||||
/// The `null` literal.
|
||||
Null,
|
||||
}
|
||||
|
||||
impl<'src> Literal<'src> {
|
||||
/// Returns a string representation of the literal.
|
||||
///
|
||||
/// This is not guaranteed to be an exact equivalent of the literal
|
||||
/// as it appears in its source expression. For example, the string
|
||||
/// representation of a floating point literal is subject to normalization,
|
||||
/// and string literals are returned without surrounding quotes.
|
||||
pub fn as_str(&self) -> Cow<'src, str> {
|
||||
match self {
|
||||
Literal::String(s) => s.clone(),
|
||||
Literal::Number(n) => Cow::Owned(n.to_string()),
|
||||
Literal::Boolean(b) => Cow::Owned(b.to_string()),
|
||||
Literal::Null => Cow::Borrowed("null"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the trivial constant evaluation of the literal.
|
||||
pub(crate) fn consteval(&self) -> Evaluation {
|
||||
match self {
|
||||
Literal::String(s) => Evaluation::String(s.to_string()),
|
||||
Literal::Number(n) => Evaluation::Number(*n),
|
||||
Literal::Boolean(b) => Evaluation::Boolean(*b),
|
||||
Literal::Null => Evaluation::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::Expr;
|
||||
|
||||
#[test]
|
||||
fn test_evaluate_constant_literals() -> Result<()> {
|
||||
use crate::Evaluation;
|
||||
|
||||
let test_cases = &[
|
||||
("'hello'", Evaluation::String("hello".to_string())),
|
||||
("'world'", Evaluation::String("world".to_string())),
|
||||
("42", Evaluation::Number(42.0)),
|
||||
("3.14", Evaluation::Number(3.14)),
|
||||
("true", Evaluation::Boolean(true)),
|
||||
("false", Evaluation::Boolean(false)),
|
||||
("null", Evaluation::Null),
|
||||
];
|
||||
|
||||
for (expr_str, expected) in test_cases {
|
||||
let expr = Expr::parse(expr_str)?;
|
||||
let result = expr.consteval().unwrap();
|
||||
assert_eq!(result, *expected, "Failed for expression: {}", expr_str);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
80
crates/github-actions-expressions/src/op.rs
Normal file
80
crates/github-actions-expressions/src/op.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//! Unary and binary operators.
|
||||
|
||||
/// Binary operations allowed in an expression.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BinOp {
|
||||
/// `expr && expr`
|
||||
And,
|
||||
/// `expr || expr`
|
||||
Or,
|
||||
/// `expr == expr`
|
||||
Eq,
|
||||
/// `expr != expr`
|
||||
Neq,
|
||||
/// `expr > expr`
|
||||
Gt,
|
||||
/// `expr >= expr`
|
||||
Ge,
|
||||
/// `expr < expr`
|
||||
Lt,
|
||||
/// `expr <= expr`
|
||||
Le,
|
||||
}
|
||||
|
||||
/// Unary operations allowed in an expression.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum UnOp {
|
||||
/// `!expr`
|
||||
Not,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Expr;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_evaluate_constant_binary_operations() -> Result<()> {
|
||||
use crate::Evaluation;
|
||||
|
||||
let test_cases = &[
|
||||
// Boolean operations
|
||||
("true && true", Evaluation::Boolean(true)),
|
||||
("true && false", Evaluation::Boolean(false)),
|
||||
("false && true", Evaluation::Boolean(false)),
|
||||
("false && false", Evaluation::Boolean(false)),
|
||||
("true || true", Evaluation::Boolean(true)),
|
||||
("true || false", Evaluation::Boolean(true)),
|
||||
("false || true", Evaluation::Boolean(true)),
|
||||
("false || false", Evaluation::Boolean(false)),
|
||||
// Equality operations
|
||||
("1 == 1", Evaluation::Boolean(true)),
|
||||
("1 == 2", Evaluation::Boolean(false)),
|
||||
("'hello' == 'hello'", Evaluation::Boolean(true)),
|
||||
("'hello' == 'world'", Evaluation::Boolean(false)),
|
||||
("true == true", Evaluation::Boolean(true)),
|
||||
("true == false", Evaluation::Boolean(false)),
|
||||
("1 != 2", Evaluation::Boolean(true)),
|
||||
("1 != 1", Evaluation::Boolean(false)),
|
||||
// Comparison operations
|
||||
("1 < 2", Evaluation::Boolean(true)),
|
||||
("2 < 1", Evaluation::Boolean(false)),
|
||||
("1 <= 1", Evaluation::Boolean(true)),
|
||||
("1 <= 2", Evaluation::Boolean(true)),
|
||||
("2 <= 1", Evaluation::Boolean(false)),
|
||||
("2 > 1", Evaluation::Boolean(true)),
|
||||
("1 > 2", Evaluation::Boolean(false)),
|
||||
("1 >= 1", Evaluation::Boolean(true)),
|
||||
("2 >= 1", Evaluation::Boolean(true)),
|
||||
("1 >= 2", Evaluation::Boolean(false)),
|
||||
];
|
||||
|
||||
for (expr_str, expected) in test_cases {
|
||||
let expr = Expr::parse(expr_str)?;
|
||||
let result = expr.consteval().unwrap();
|
||||
assert_eq!(result, *expected, "Failed for expression: {}", expr_str);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
use std::{ops::Deref, sync::LazyLock};
|
||||
|
||||
use github_actions_expressions::{
|
||||
BinOp, Call, Expr, SpannedExpr, UnOp,
|
||||
Expr, SpannedExpr,
|
||||
call::Call,
|
||||
context::{Context, ContextPattern},
|
||||
op::{BinOp, UnOp},
|
||||
};
|
||||
use github_actions_models::{
|
||||
common::If,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use github_actions_expressions::{Call, Expr, SpannedExpr};
|
||||
use github_actions_expressions::{Expr, SpannedExpr, call::Call};
|
||||
|
||||
use crate::{
|
||||
finding::{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
use std::{env, ops::Deref, sync::LazyLock, vec};
|
||||
|
||||
use fst::Map;
|
||||
use github_actions_expressions::{Expr, Literal, context::Context};
|
||||
use github_actions_expressions::{Expr, context::Context, literal::Literal};
|
||||
use github_actions_models::{
|
||||
common::{EnvValue, RepositoryUses, Uses, expr::LoE},
|
||||
workflow::job::Strategy,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use github_actions_expressions::{Call, Expr, SpannedExpr, context::Context};
|
||||
use github_actions_expressions::{Expr, SpannedExpr, call::Call, context::Context};
|
||||
|
||||
use crate::{
|
||||
Confidence, Severity,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use github_actions_expressions::{Call, Expr, Literal, SpannedExpr, context::Context};
|
||||
use github_actions_expressions::{
|
||||
Expr, SpannedExpr, call::Call, context::Context, literal::Literal,
|
||||
};
|
||||
use github_actions_models::common::If;
|
||||
|
||||
use super::{Audit, AuditLoadError, AuditState, audit_meta};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue