From 5e5502704a41e9864cfdb6f9e45f48fbd23b8714 Mon Sep 17 00:00:00 2001 From: qraqras Date: Fri, 27 Feb 2026 05:28:52 +0000 Subject: [PATCH 1/2] feat: CICD --- .github/workflows/ci.yml | 42 +++++++++++++++++++++++++ .github/workflows/release.yml | 58 +++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ec720d8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Clippy + run: cargo clippy --all-targets -- -D warnings + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e58af3d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,58 @@ +name: Release + +on: + push: + tags: + - "v*" + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Run tests + run: cargo test --verbose + + publish: + needs: test + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Verify version matches tag + run: | + CARGO_VERSION="v$(cargo metadata --no-deps --format-version 1 | python3 -c 'import sys,json; print(json.load(sys.stdin)["packages"][0]["version"])')" + TAG="${GITHUB_REF#refs/tags/}" + if [ "$CARGO_VERSION" != "$TAG" ]; then + echo "::error::Cargo.toml version ($CARGO_VERSION) does not match tag ($TAG)" + exit 1 + fi + + - name: Publish to crates.io + run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true diff --git a/Cargo.lock b/Cargo.lock index f537173..37fc483 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,4 @@ version = 4 [[package]] name = "pydocstring" -version = "0.1.0" +version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index fcae323..c7802a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pydocstring" -version = "0.1.0" +version = "0.0.1" edition = "2024" authors = ["Ryuma Asai"] description = "A fast, zero-dependency Rust parser for Python docstrings (NumPy and Google styles) with full AST and source location tracking" From 4986d1fcfd925519eda3a4b1ddaa0375767159a9 Mon Sep 17 00:00:00 2001 From: qraqras Date: Fri, 27 Feb 2026 05:46:10 +0000 Subject: [PATCH 2/2] fix: CI --- examples/parse_google.rs | 25 ++++++++++++++++++++----- examples/parse_numpy.rs | 13 ++++++++++--- examples/test_ret.rs | 8 ++++++-- src/styles/google/ast.rs | 6 +++++- src/styles/numpy/parser.rs | 20 ++++++++++---------- 5 files changed, 51 insertions(+), 21 deletions(-) diff --git a/examples/parse_google.rs b/examples/parse_google.rs index 3ee7ca8..a6a63be 100644 --- a/examples/parse_google.rs +++ b/examples/parse_google.rs @@ -43,10 +43,16 @@ Raises: .collect(); println!("\nArgs ({}):", args.len()); for arg in &args { - let type_str = arg.r#type.as_ref().map(|t| t.source_text(&doc.source)).unwrap_or("?"); + let type_str = arg + .r#type + .as_ref() + .map(|t| t.source_text(&doc.source)) + .unwrap_or("?"); println!( " {} ({}): {}", - arg.name.source_text(&doc.source), type_str, arg.description.source_text(&doc.source) + arg.name.source_text(&doc.source), + type_str, + arg.description.source_text(&doc.source) ); } @@ -63,7 +69,11 @@ Raises: .as_ref() .map(|t| t.source_text(&doc.source)) .unwrap_or("?"); - println!("\nReturns: {}: {}", type_str, ret.description.source_text(&doc.source)); + println!( + "\nReturns: {}: {}", + type_str, + ret.description.source_text(&doc.source) + ); } let raises: Vec<_> = doc @@ -80,7 +90,11 @@ Raises: .collect(); println!("\nRaises ({}):", raises.len()); for exc in &raises { - println!(" {}: {}", exc.r#type.source_text(&doc.source), exc.description.source_text(&doc.source)); + println!( + " {}: {}", + exc.r#type.source_text(&doc.source), + exc.description.source_text(&doc.source) + ); } let all_sections: Vec<_> = doc @@ -95,7 +109,8 @@ Raises: for section in &all_sections { println!( " {} (header: {:?})", - section.header.name.source_text(&doc.source), section.header.range + section.header.name.source_text(&doc.source), + section.header.range ); } } diff --git a/examples/parse_numpy.rs b/examples/parse_numpy.rs index 9463c64..9d71a45 100644 --- a/examples/parse_numpy.rs +++ b/examples/parse_numpy.rs @@ -57,8 +57,11 @@ Examples NumPySectionBody::Parameters(params) => { println!("\nParameters:"); for param in params { - let names: Vec<&str> = - param.names.iter().map(|n| n.source_text(&doc.source)).collect(); + let names: Vec<&str> = param + .names + .iter() + .map(|n| n.source_text(&doc.source)) + .collect(); println!( " - {:?}: {:?}", names, @@ -80,7 +83,11 @@ Examples NumPySectionBody::Raises(excs) => { println!("\nRaises:"); for exc in excs { - println!(" - {}: {}", exc.r#type.source_text(&doc.source), exc.description.source_text(&doc.source)); + println!( + " - {}: {}", + exc.r#type.source_text(&doc.source), + exc.description.source_text(&doc.source) + ); } } NumPySectionBody::Notes(text) => { diff --git a/examples/test_ret.rs b/examples/test_ret.rs index ffbd2b7..a2f9259 100644 --- a/examples/test_ret.rs +++ b/examples/test_ret.rs @@ -1,5 +1,5 @@ -use pydocstring::google::parse_google; use pydocstring::GoogleSectionBody; +use pydocstring::google::parse_google; fn main() { let input = "\ @@ -22,7 +22,11 @@ Returns: for (idx, item) in doc.items.iter().enumerate() { match item { pydocstring::GoogleDocstringItem::Section(s) => { - println!("Item {}: Section {:?}", idx, s.header.name.source_text(&doc.source)); + println!( + "Item {}: Section {:?}", + idx, + s.header.name.source_text(&doc.source) + ); if let GoogleSectionBody::Returns(ref ret) = s.body { let type_str = ret.return_type.as_ref().map(|t| t.source_text(&doc.source)); let d = &ret.description.source_text(&doc.source); diff --git a/src/styles/google/ast.rs b/src/styles/google/ast.rs index c71bb88..21d4c16 100644 --- a/src/styles/google/ast.rs +++ b/src/styles/google/ast.rs @@ -458,6 +458,10 @@ impl Default for GoogleDocstring { impl fmt::Display for GoogleDocstring { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "GoogleDocstring(summary: {})", self.summary.source_text(&self.source)) + write!( + f, + "GoogleDocstring(summary: {})", + self.summary.source_text(&self.source) + ) } } diff --git a/src/styles/numpy/parser.rs b/src/styles/numpy/parser.rs index 98deebd..c76c5b0 100644 --- a/src/styles/numpy/parser.rs +++ b/src/styles/numpy/parser.rs @@ -600,12 +600,12 @@ fn parse_name_and_type( let seg_abs = type_abs_start + seg_offset + (seg_raw.len() - seg_raw.trim_start().len()); optional = Some(TextRange::from_offset_len(seg_abs, "optional".len())); - } else if seg.starts_with("default") { + } else if let Some(stripped) = seg.strip_prefix("default") { let ws_lead = seg_raw.len() - seg_raw.trim_start().len(); let kw_abs = type_abs_start + seg_offset + ws_lead; default_keyword = Some(TextRange::from_offset_len(kw_abs, "default".len())); - let after_kw = seg["default".len()..].trim_start(); + let after_kw = stripped.trim_start(); if let Some(rest) = after_kw.strip_prefix('=') { let sep_pos = seg.find('=').unwrap(); let sep_abs = kw_abs + sep_pos; @@ -986,10 +986,10 @@ fn parse_references( let end_col = current_col + content.lines().last().unwrap_or("").len(); refs.push(crate::styles::numpy::ast::NumPyReference { range: cursor.make_range(start_l, current_col, end_l, end_col), - directive_marker: current_directive_marker.clone(), - open_bracket: current_open_bracket.clone(), - number: current_number.clone(), - close_bracket: current_close_bracket.clone(), + directive_marker: current_directive_marker, + open_bracket: current_open_bracket, + number: current_number, + close_bracket: current_close_bracket, content: cursor.make_range(start_l, current_col, end_l, end_col), }); } @@ -1035,10 +1035,10 @@ fn parse_references( let end_col = current_col + content.lines().last().unwrap_or("").len(); refs.push(crate::styles::numpy::ast::NumPyReference { range: cursor.make_range(start_l, current_col, end_l, end_col), - directive_marker: current_directive_marker.clone(), - open_bracket: current_open_bracket.clone(), - number: current_number.clone(), - close_bracket: current_close_bracket.clone(), + directive_marker: current_directive_marker, + open_bracket: current_open_bracket, + number: current_number, + close_bracket: current_close_bracket, content: cursor.make_range(start_l, current_col, end_l, end_col), }); current_content_lines.clear();