Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# =============================================================================
# Arborist - Rust CI Workflow
# =============================================================================
#
# Runs cargo build/test across ubuntu/macOS/Windows plus clippy, fmt and doc
# gates on every push to main and every pull request. Path-filtered so changes
# to .straymark/ docs or governance files do not trigger Rust compilation.
#
# https://strangedays.tech
# =============================================================================

name: Rust CI

on:
push:
branches: [main]
paths:
- '**/*.rs'
- 'Cargo.toml'
- 'Cargo.lock'
- 'README.md' # embedded in src/lib.rs via #![doc = include_str!(...)]
- '.github/workflows/rust-ci.yml'
pull_request:
branches: [main]
paths:
- '**/*.rs'
- 'Cargo.toml'
- 'Cargo.lock'
- 'README.md'
- '.github/workflows/rust-ci.yml'
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

jobs:
# ===========================================================================
# Build and test on the three major desktop OSes
# ===========================================================================
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v5

- name: Install stable Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo registry, index and target
uses: Swatinem/rust-cache@v2

- name: cargo build --all-features
run: cargo build --all-features --verbose

- name: cargo test --all-features
run: cargo test --all-features --verbose

# ===========================================================================
# Minimal-feature build: catches code that implicitly depends on a feature
# ===========================================================================
build-minimal:
name: Build (--no-default-features)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: minimal
- run: cargo build --no-default-features --verbose

# ===========================================================================
# Clippy lint gate
# ===========================================================================
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
with:
key: clippy
- run: cargo clippy --all-features --all-targets -- -D warnings

# ===========================================================================
# Format gate
# ===========================================================================
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check

# ===========================================================================
# Documentation build (catches broken intra-doc links)
# ===========================================================================
doc:
name: Docs
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: -D warnings
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: doc
- run: cargo doc --all-features --no-deps
4 changes: 3 additions & 1 deletion examples/dump_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ fn print_tree(node: &tree_sitter::Node, source: &[u8], indent: usize) {
}
})
});
let field_str = field.map(|f| format!(" [field: {}]", f)).unwrap_or_default();
let field_str = field
.map(|f| format!(" [field: {}]", f))
.unwrap_or_default();
println!(
"{}{} ({}){} | {}",
prefix,
Expand Down
6 changes: 1 addition & 5 deletions src/languages/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,7 @@ impl LanguageProfile for CProfile {
&["comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
find_function_declarator_name(node, source)
}

Expand Down
6 changes: 1 addition & 5 deletions src/languages/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@ impl LanguageProfile for CppProfile {
&["comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
find_function_declarator_name(node, source)
}

Expand Down
6 changes: 1 addition & 5 deletions src/languages/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ impl LanguageProfile for CSharpProfile {
&["comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
node.child_by_field_name("name")
.and_then(|n| n.utf8_text(source).ok())
.map(|s| s.to_string())
Expand Down
19 changes: 12 additions & 7 deletions src/languages/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ impl LanguageProfile for GoProfile {
&["comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
node.child_by_field_name("name")
.and_then(|n| n.utf8_text(source).ok())
.map(|s| s.to_string())
Expand All @@ -70,10 +66,19 @@ impl LanguageProfile for GoProfile {
}

fn match_construct_nodes(&self) -> &[&str] {
&["expression_switch_statement", "type_switch_statement", "select_statement"]
&[
"expression_switch_statement",
"type_switch_statement",
"select_statement",
]
}

fn match_arm_nodes(&self) -> &[&str] {
&["expression_case", "default_case", "type_case", "communication_case"]
&[
"expression_case",
"default_case",
"type_case",
"communication_case",
]
}
}
6 changes: 1 addition & 5 deletions src/languages/java.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,7 @@ impl LanguageProfile for JavaProfile {
&["line_comment", "block_comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
node.child_by_field_name("name")
.and_then(|n| n.utf8_text(source).ok())
.map(|s| s.to_string())
Expand Down
12 changes: 6 additions & 6 deletions src/languages/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ impl LanguageProfile for JavaScriptProfile {
}

fn lambda_nodes(&self) -> &[&str] {
&["arrow_function", "function_expression", "generator_function"]
&[
"arrow_function",
"function_expression",
"generator_function",
]
}

fn comment_nodes(&self) -> &[&str] {
&["comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
node.child_by_field_name("name")
.and_then(|n| n.utf8_text(source).ok())
.map(|s| s.to_string())
Expand Down
6 changes: 1 addition & 5 deletions src/languages/kotlin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ impl LanguageProfile for KotlinProfile {
&["line_comment", "multiline_comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
node.child_by_field_name("name")
.and_then(|n| n.utf8_text(source).ok())
.map(|s| s.to_string())
Expand Down
46 changes: 23 additions & 23 deletions src/languages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,30 @@
use crate::error::ArboristError;
use crate::types::Language;

#[cfg(feature = "rust")]
pub mod rust;
#[cfg(feature = "python")]
pub mod python;
#[cfg(feature = "javascript")]
pub mod javascript;
#[cfg(feature = "typescript")]
pub mod typescript;
#[cfg(feature = "java")]
pub mod java;
#[cfg(feature = "csharp")]
pub mod csharp;
#[cfg(feature = "cpp")]
pub mod cpp;
#[cfg(feature = "c")]
pub mod c;
#[cfg(feature = "cpp")]
pub mod cpp;
#[cfg(feature = "csharp")]
pub mod csharp;
#[cfg(feature = "go")]
pub mod go;
#[cfg(feature = "php")]
pub mod php;
#[cfg(feature = "java")]
pub mod java;
#[cfg(feature = "javascript")]
pub mod javascript;
#[cfg(feature = "kotlin")]
pub mod kotlin;
#[cfg(feature = "php")]
pub mod php;
#[cfg(feature = "python")]
pub mod python;
#[cfg(feature = "rust")]
pub mod rust;
#[cfg(feature = "swift")]
pub mod swift;
#[cfg(feature = "typescript")]
pub mod typescript;

/// Trait that defines how a language's AST maps to control-flow concepts.
///
Expand Down Expand Up @@ -87,11 +87,7 @@ pub trait LanguageProfile {
fn comment_nodes(&self) -> &[&str];

/// Extract the function name from an AST node.
fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String>;
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String>;

/// Get the tree-sitter `Language` for parsing.
fn parser_language(&self) -> tree_sitter::Language;
Expand Down Expand Up @@ -138,7 +134,9 @@ pub trait LanguageProfile {
///
/// Returns `Err(UnrecognizedExtension)` if the extension is unknown, or
/// `Err(LanguageNotEnabled)` if the language is known but its feature is off.
pub fn profile_for_extension(ext: &str) -> Result<(Language, Box<dyn LanguageProfile>), ArboristError> {
pub fn profile_for_extension(
ext: &str,
) -> Result<(Language, Box<dyn LanguageProfile>), ArboristError> {
let ext_lower = ext.to_lowercase();
let ext_ref = ext_lower.as_str();

Expand Down Expand Up @@ -169,7 +167,9 @@ pub fn profile_for_extension(ext: &str) -> Result<(Language, Box<dyn LanguagePro
/// Get the profile for a known language.
///
/// Returns `Err(LanguageNotEnabled)` if the feature flag is not compiled in.
pub fn profile_for_language(language: Language) -> Result<(Language, Box<dyn LanguageProfile>), ArboristError> {
pub fn profile_for_language(
language: Language,
) -> Result<(Language, Box<dyn LanguageProfile>), ArboristError> {
let profile: Box<dyn LanguageProfile> = match language {
#[cfg(feature = "rust")]
Language::Rust => Box::new(rust::RustProfile),
Expand Down
13 changes: 7 additions & 6 deletions src/languages/php.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,7 @@ impl LanguageProfile for PhpProfile {
&["comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
node.child_by_field_name("name")
.and_then(|n| n.utf8_text(source).ok())
.map(|s| s.to_string())
Expand Down Expand Up @@ -92,6 +88,11 @@ impl LanguageProfile for PhpProfile {
}

fn match_arm_nodes(&self) -> &[&str] {
&["case_statement", "default_statement", "match_conditional_expression", "match_default_expression"]
&[
"case_statement",
"default_statement",
"match_conditional_expression",
"match_default_expression",
]
}
}
13 changes: 7 additions & 6 deletions src/languages/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ impl LanguageProfile for PythonProfile {
}

fn nesting_nodes(&self) -> &[&str] {
&["if_statement", "for_statement", "while_statement", "match_statement"]
&[
"if_statement",
"for_statement",
"while_statement",
"match_statement",
]
}

fn boolean_operators(&self) -> &[&str] {
Expand All @@ -42,11 +47,7 @@ impl LanguageProfile for PythonProfile {
&["comment"]
}

fn extract_function_name(
&self,
node: &tree_sitter::Node,
source: &[u8],
) -> Option<String> {
fn extract_function_name(&self, node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
node.child_by_field_name("name")
.and_then(|n| n.utf8_text(source).ok())
.map(|s| s.to_string())
Expand Down
Loading
Loading