Skip to content

Conversation

@ItzNotABug
Copy link
Member

@ItzNotABug ItzNotABug commented Dec 27, 2025

What does this PR do?

Adds Rust SDK support 🦀


Some notable arch related information -

  1. Uses arc-swap to maintain proper mutability of Client like how other SDKs manage. Its usually auto-handled for others but this needs to be explicitly managed. Usecase is not frequent but always a possibility where you want to change something on a client, add headers, etc. This allows us to redefine client and the service with new instances. Its not cheap but definitely not expensive as well since this is not a normal thing to change/add/update headers or client itself too frequently.

  2. Uses tokio, the obvious choice and reqwest for multipart.

Test Plan

Manual, E2Es.

Related PRs and Issues

Have you read the Contributing Guidelines on issues?

Yes.

Summary by CodeRabbit

  • New Features
    • Full Rust SDK added: client, services, models, enums, error handling, file/ID utilities, query/operator/permission/role helpers, and usage examples.
  • CI / Automation
    • Rust added to build/test matrix and new Rust-specific CI/workflows for validation, publishing, stale/auto-close.
  • Examples & Docs
    • Rust README, changelog, Cargo package template, and example usage added.
  • Tests
    • Rust unit/integration tests and a test runner/script added.
  • Chores
    • Generator and lint/gitignore updated to include Rust.

✏️ Tip: You can customize this high-level summary in your review settings.

@ItzNotABug ItzNotABug self-assigned this Dec 27, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 27, 2025

Walkthrough

Adds comprehensive Rust SDK generation and CI support. Introduces a new PHP language backend Appwrite\SDK\Language\Rust with type mapping, template filters, naming rules, and file scaffolding. Adds ~30 Rust Twig templates (Cargo.toml, README, CHANGELOG, LICENSE, .gitignore, examples, and extensive src templates for client, services, models, enums, error, input_file, id, operator, permission, query, role, tests). Adds GitHub Actions templates for publish/autoclose/stale, updates CI matrices and build/test steps to include Rust, example generator changes, a Rust test runner script, and a PHP test class for running the Rust test suite.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Rust 🦀' is vague and does not clearly convey the scope or nature of the changes; it uses an emoji and lacks descriptive information about what was implemented. Use a more descriptive title such as 'Add Rust SDK support' or 'Implement Rust language backend for SDK generator' to clearly communicate the primary change.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
templates/rust/src/operator.rs.twig (2)

58-70: Remove redundant as_i64 branch.

After validate_numeric passes, as_f64() is guaranteed to return Some for any JSON number (integers are converted to f64). The integer 0 converts precisely to 0.0, so lines 65-68 are unreachable.

♻️ Proposed simplification
 fn validate_nonzero_divisor(value: &Value, param_name: &str) {
     validate_numeric(value, param_name);
 
-    if let Some(num) = value.as_f64() {
-        if num == 0.0 {
-            panic!("{} cannot be zero", param_name);
-        }
-    } else if let Some(num) = value.as_i64() {
-        if num == 0 {
-            panic!("{} cannot be zero", param_name);
-        }
+    if value.as_f64().unwrap() == 0.0 {
+        panic!("{} cannot be zero", param_name);
     }
 }

385-396: Consider adding tests for validation edge cases.

The panic tests for divide-by-zero are good. Consider adding tests for the validate_numeric rejection paths to ensure non-numeric inputs and NaN/Infinity are properly rejected.

💡 Example edge case tests
#[test]
#[should_panic(expected = "must be a number")]
fn test_increment_by_non_numeric() {
    increment_by(Value::String("not a number".to_string()));
}

#[test]
#[should_panic(expected = "cannot be NaN or Infinity")]
fn test_multiply_infinity() {
    multiply(f64::INFINITY);
}

#[test]
#[should_panic(expected = "cannot be NaN or Infinity")]
fn test_divide_nan() {
    divide(f64::NAN);
}
templates/rust/src/client.rs.twig (1)

400-440: Minor code duplication in call_location method.

The header application logic is duplicated. When rebuilding the request for GET with params (line 430), headers are re-applied (lines 431-436) after already being set earlier (lines 413-419). This works correctly but could be streamlined.

♻️ Suggested refactor
     pub async fn call_location(
         &self,
         method: Method,
         path: &str,
         headers: Option<HashMap<String, String>>,
         params: Option<HashMap<String, Value>>,
     ) -> Result<String> {
         let state = self.state.load_full();
         let url = format!("{}{}", state.config.endpoint, path);
+        let final_url;
+        let final_method;
 
-        let mut request_builder = state.http_no_redirect.request(method.clone(), &url);
-
-        request_builder = request_builder.headers(state.config.headers.clone());
-
-        if let Some(ref custom_headers) = headers {
-            for (key, value) in custom_headers {
-                request_builder = request_builder.header(key, value);
-            }
-        }
-
         if let Some(params) = params {
             if method == Method::GET {
                 let mut url_with_params = Url::parse(&url).map_err(|e| {{ spec.title | caseUcfirst }}Error::new(0, format!("Invalid URL: {}", e), None, String::new()))?;
                 {
                     let mut query_pairs = url_with_params.query_pairs_mut();
                     for (key, value) in Self::flatten_query_params(&params) {
                         query_pairs.append_pair(&key, &value);
                     }
                 }
-                request_builder = state.http_no_redirect.request(method, url_with_params);
-                request_builder = request_builder.headers(state.config.headers.clone());
-                if let Some(ref custom_headers) = headers {
-                    for (key, value) in custom_headers {
-                        request_builder = request_builder.header(key, value);
-                    }
-                }
+                final_url = url_with_params.to_string();
+                final_method = method;
             } else {
-                request_builder = request_builder.json(&params);
+                final_url = url;
+                final_method = method;
             }
+        } else {
+            final_url = url;
+            final_method = method;
         }
+
+        let mut request_builder = state.http_no_redirect.request(final_method, &final_url);
+        request_builder = request_builder.headers(state.config.headers.clone());
+
+        if let Some(ref custom_headers) = headers {
+            for (key, value) in custom_headers {
+                request_builder = request_builder.header(key, value);
+            }
+        }
+
+        // Add JSON body for non-GET methods with params
+        if params.is_some() && method != Method::GET {
+            request_builder = request_builder.json(&params.unwrap());
+        }
src/SDK/Language/Rust.php (2)

685-693: Unused $response variable in foreach loop.

The $response variable is declared but never used. Since only the response code matters for the empty response check, you can use array_keys() instead.

♻️ Suggested refactor
     protected function isEmptyResponse(array $responses): bool
     {
-        foreach ($responses as $code => $response) {
+        foreach (array_keys($responses) as $code) {
             if (!in_array((int)$code, [204, 205])) {
                 return false;
             }
         }
         return !empty($responses);
     }

647-683: Unused parameters are acceptable for interface consistency.

The $spec, $namespace, and $generic parameters flagged by static analysis are present for Twig filter signature consistency. Adding a brief docblock note would clarify this intent for future maintainers.

📝 Suggested documentation
     /**
+     * Get the return type for a method.
+     *
+     * Note: $spec, $namespace, and $generic parameters are included for
+     * Twig filter signature consistency but are not used in this implementation.
+     *
      * `@param` array $method
      * `@param` array $spec
      * `@param` string $namespace
      * `@param` string $generic
      * `@return` string
      */
     protected function getReturnType(

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf9f274 and fd2fefa.

📒 Files selected for processing (9)
  • src/SDK/Language/Rust.php
  • templates/rust/.github/workflows/publish.yml.twig
  • templates/rust/src/client.rs.twig
  • templates/rust/src/input_file.rs.twig
  • templates/rust/src/operator.rs.twig
  • templates/rust/src/permission.rs.twig
  • templates/rust/src/query.rs.twig
  • templates/rust/tests/tests.rs
  • tests/Rust183Test.php
🚧 Files skipped from review as they are similar to previous changes (5)
  • templates/rust/tests/tests.rs
  • templates/rust/src/permission.rs.twig
  • tests/Rust183Test.php
  • templates/rust/.github/workflows/publish.yml.twig
  • templates/rust/src/query.rs.twig
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-25T12:47:16.427Z
Learnt from: ItzNotABug
Repo: appwrite/sdk-generator PR: 1273
File: src/Spec/Swagger2.php:167-179
Timestamp: 2025-12-25T12:47:16.427Z
Learning: In src/Spec/Swagger2.php, when parsing `x-oneOf` entries from OpenAPI specs, the `$ref` key is always guaranteed to be present by design. If it's missing, it indicates a broader spec validation issue rather than a normal edge case to handle.

Applied to files:

  • src/SDK/Language/Rust.php
🧬 Code graph analysis (1)
src/SDK/Language/Rust.php (1)
src/SDK/Language.php (1)
  • Language (5-295)
🪛 PHPMD (2.15.0)
src/SDK/Language/Rust.php

279-279: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


415-415: Avoid unused parameters such as '$lang'. (undefined)

(UnusedFormalParameter)


543-543: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


649-649: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


650-650: Avoid unused parameters such as '$namespace'. (undefined)

(UnusedFormalParameter)


651-651: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


687-687: Avoid unused local variables such as '$response'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Python310)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Python39)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: swift (server)
  • GitHub Check: kotlin (server)
  • GitHub Check: dotnet (server)
  • GitHub Check: rust (server)
  • GitHub Check: apple (client)
  • GitHub Check: android (client)
🔇 Additional comments (11)
templates/rust/src/operator.rs.twig (5)

1-31: LGTM!

The Condition enum is well-structured with appropriate trait derivations and exhaustive as_str() conversion.


33-45: LGTM!

The OperatorData struct and parse_operator helper are cleanly implemented with appropriate visibility.


72-154: LGTM!

Arithmetic operators are correctly implemented with appropriate validation for numeric inputs and division-by-zero protection.


156-212: LGTM!

Array operators are well-implemented with consistent serialization patterns and proper use of the Condition enum.


214-239: LGTM!

String, boolean, and date operators are cleanly implemented.

templates/rust/src/input_file.rs.twig (1)

1-298: LGTM!

The InputFile implementation is well-designed for the SDK's file upload needs. The dual-source approach (Path vs Bytes) with async/sync variants provides good flexibility. The ChunkedReader correctly handles both streaming from files and slicing from memory buffers.

A few observations (not blocking):

  • The is_empty() method uses sync I/O even though the rest of the API has async variants, but this is acceptable for a quick existence check.
  • The From impls default to "unknown" filename, which is documented by the test.
templates/rust/src/client.rs.twig (2)

850-854: Subtle: empty response deserializes as JSON null.

The empty response handling deserializes b"null" which works for () (the typical return type for 204/205 responses) and Option<T>. However, if a caller somehow expects a concrete struct from an empty response, this will fail at runtime with a deserialization error.

This is correct behavior given the SDK's return type generation logic, but worth noting for maintainability.


887-906: LGTM!

The tests verify basic client creation and builder pattern functionality.

src/SDK/Language/Rust.php (3)

38-94: LGTM!

The Rust keywords list is comprehensive and includes all strict, reserved, and contextual keywords from the Rust reference, including the 2024 edition addition (gen).


100-137: LGTM!

The identifier overrides correctly map Rust keywords to raw identifier syntax (r#keyword), which is the proper way to use reserved words as identifiers in Rust.


566-612: Nice ergonomics improvement with impl Into patterns.

Using impl Into<String> and impl IntoIterator<Item = impl Into<String>> for input types provides excellent Rust DX, allowing users to pass &str, String, slices, vectors, or arrays interchangeably.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

update: djlint config for rust, readme.
@ItzNotABug ItzNotABug changed the title Rust 🚧 Rust 🦀 Dec 28, 2025
@ItzNotABug ItzNotABug marked this pull request as ready for review December 28, 2025 08:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (15)
templates/rust/.github/workflows/publish.yml.twig (1)

16-17: Consider consistency in Rust toolchain versions.

This workflow uses @stable while the validation workflow uses @1.83.0. This inconsistency could lead to publishing with a different Rust version than what was tested in CI.

Consider either:

  1. Using the same pinned version (@1.83.0) for consistency, or
  2. Documenting why stable is preferred for publishing
🔎 Proposed fix to align with CI validation version
       - name: Setup Rust
-        uses: dtolnay/rust-toolchain@stable
+        uses: dtolnay/[email protected]
example.php (1)

270-276: Consider setting Rust-specific configuration.

The Rust SDK generation uses only default configuration. Based on the Rust.php language class, there's a setCratePackage() method available. Consider whether a specific crate package name should be set, similar to how other SDKs configure their package names (e.g., Dart uses setPackageName('dart_appwrite')).

🔎 Example configuration with crate package name
     // Rust
     if (!$requestedSdk || $requestedSdk === 'rust') {
-        $sdk = new SDK(new Rust(), new Swagger2($spec));
+        $rust = new Rust();
+        $rust->setCratePackage('appwrite');
+        $sdk = new SDK($rust, new Swagger2($spec));
         configureSDK($sdk);
         $sdk->generate(__DIR__ . '/examples/rust');
     }
templates/rust/tests/tests.rs (2)

102-105: Clarify duplicate test calls.

Lines 102-103 and 104-105 each call the same test functions twice (test_general_upload and test_large_upload). If this is intentional to test idempotency or retry behavior, consider adding a comment. Otherwise, these might be unintentional duplicates.

     test_general_upload(client, string_in_array).await?;
-    test_general_upload(client, string_in_array).await?;
     test_large_upload(client, string_in_array).await?;
-    test_large_upload(client, string_in_array).await?;

206-269: Consider adding assertions for query tests.

The query tests only print outputs but don't verify correctness. While this might be sufficient for integration testing against a mock API that validates the output format, consider whether some basic assertions would improve test reliability.

templates/rust/CHANGELOG.md.twig (1)

44-50: Ensure dependency versions match Cargo.toml.

The dependency versions listed in the changelog should match those in Cargo.toml.twig. When updating dependency versions in Cargo.toml, remember to update this changelog template as well.

templates/rust/src/id.rs.twig (1)

16-18: Consider graceful error handling instead of panic.

The .expect("Time went backwards") will panic if system time moves backwards (e.g., NTP correction, VM snapshot restore). For a production SDK, consider returning a Result or using a fallback strategy.

🔎 Proposed alternatives

Option 1: Return Result from the function

-    pub fn unique_with_padding(padding: usize) -> String {
+    pub fn unique_with_padding(padding: usize) -> Result<String, std::time::SystemTimeError> {
         let now = SystemTime::now()
-            .duration_since(UNIX_EPOCH)
-            .expect("Time went backwards");
+            .duration_since(UNIX_EPOCH)?;

Option 2: Use a fallback to maintain the current API

         let now = SystemTime::now()
             .duration_since(UNIX_EPOCH)
-            .expect("Time went backwards");
+            .unwrap_or_else(|_| std::time::Duration::from_secs(0));
templates/rust/src/permission.rs.twig (1)

15-38: Update misleading documentation comments.

The doc comments say "for any user" but all methods accept a role parameter that determines the specific role/user. The comments should reflect that these methods create permissions for the specified role.

🔎 Proposed documentation fix
-    /// Read permission for any user
+    /// Read permission for the specified role
     pub fn read(role: impl std::fmt::Display) -> Self {
         Self::new(format!("read(\"{}\")", role))
     }

-    /// Write permission for any user
+    /// Write permission for the specified role
     pub fn write(role: impl std::fmt::Display) -> Self {
         Self::new(format!("write(\"{}\")", role))
     }

-    /// Create permission for any user
+    /// Create permission for the specified role
     pub fn create(role: impl std::fmt::Display) -> Self {
         Self::new(format!("create(\"{}\")", role))
     }

-    /// Update permission for any user
+    /// Update permission for the specified role
     pub fn update(role: impl std::fmt::Display) -> Self {
         Self::new(format!("update(\"{}\")", role))
     }

-    /// Delete permission for any user
+    /// Delete permission for the specified role
     pub fn delete(role: impl std::fmt::Display) -> Self {
         Self::new(format!("delete(\"{}\")", role))
     }
templates/rust/src/models/model.rs.twig (1)

51-77: Tests rely on Default trait that may not be meaningful for all models.

The test module assumes all field types implement Default. For models with required fields of custom types (e.g., nested models), Default::default() may produce invalid/empty data that could cause issues during serialization roundtrips if the server rejects empty required fields.

Consider adding a comment noting this limitation, or generating more meaningful test data for required fields.

templates/rust/src/input_file.rs.twig (1)

163-168: is_empty() returns false for path-based files without checking actual size.

For InputFileSource::Path, returning false unconditionally could be misleading if the file is actually empty (0 bytes). Consider using size_sync() or documenting this behavior.

🔎 Suggested improvement
     pub fn is_empty(&self) -> bool {
         match &self.source {
             InputFileSource::Bytes { data } => data.is_empty(),
-            InputFileSource::Path { .. } => false,
+            InputFileSource::Path { path } => {
+                std::fs::metadata(path).map(|m| m.len() == 0).unwrap_or(true)
+            }
         }
     }
templates/rust/src/client.rs.twig (3)

124-136: Consider returning Result instead of panicking on invalid endpoint.

Panicking in set_endpoint for invalid URLs could cause unexpected crashes in user code. A Result return type or a non-builder method that validates would be more robust.

🔎 Alternative approach
-    pub fn set_endpoint<S: Into<String>>(&self, endpoint: S) -> Self {
+    pub fn set_endpoint<S: Into<String>>(&self, endpoint: S) -> Result<Self, crate::error::{{ spec.title | caseUcfirst }}Error> {
         let endpoint = endpoint.into();
         if !endpoint.starts_with("http://") && !endpoint.starts_with("https://") {
-            panic!("Invalid endpoint URL: {}. Endpoint must start with http:// or https://", endpoint);
+            return Err(crate::error::{{ spec.title | caseUcfirst }}Error::new(
+                0,
+                format!("Invalid endpoint URL: {}. Endpoint must start with http:// or https://", endpoint),
+                None,
+                String::new(),
+            ));
         }
         // ... rest of method
-        self.clone()
+        Ok(self.clone())
     }

216-234: Silent failure in add_header could hide configuration errors.

When header name or value parsing fails, the method silently proceeds without adding the header. Consider logging a warning or returning a Result to help users debug configuration issues.


375-479: call_location creates a new HTTP client per invocation.

Lines 387-392 build a new reqwest::Client for each call_location call. While this is needed for the no-redirect policy, client creation has overhead. Consider caching a no-redirect client in ClientState if this method is called frequently.

src/SDK/Language/Rust.php (1)

539-552: Unused parameters flagged by static analysis are likely intentional for interface consistency.

The $spec, $namespace, $generic parameters in getPropertyType, getReturnType, and hasGenericType are currently unused but may be required by the parent class interface or reserved for future use. If intentional, consider adding @SuppressWarnings or a brief comment.

Also applies to: 643-667, 674-695

templates/rust/src/query.rs.twig (2)

251-257: Unnecessary clone in Display implementation.

to_value(self) takes ownership, but Display::fmt only has &self. The current approach clones the entire Query. Consider deriving Serialize for Query directly or implementing a method that serializes from &self.

🔎 Alternative: derive Serialize directly
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Query {
    method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    attribute: Option<String>,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    values: Vec<Value>,
}

impl std::fmt::Display for Query {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let json_str = serde_json::to_string(self)
            .unwrap_or_else(|_| String::new());
        write!(f, "{}", json_str)
    }
}

210-232: or and and methods require pre-serialized JSON strings.

The API requires users to serialize Query objects to strings first, then pass them to or/and. This is somewhat awkward. Consider also accepting Vec<Query> directly.

🔎 Additional overload suggestion
/// Combine queries with OR (accepts Query objects directly)
pub fn or_queries(queries: impl IntoIterator<Item = Query>) -> Self {
    let values: Vec<Value> = queries.into_iter().map(|q| q.to_value()).collect();
    Self::new("or".to_string(), None, values)
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc3531b and d5ade7f.

📒 Files selected for processing (32)
  • .github/workflows/sdk-build-validation.yml
  • .github/workflows/tests.yml
  • example.php
  • pyproject.toml
  • src/SDK/Language/Rust.php
  • templates/rust/.github/workflows/autoclose.yml
  • templates/rust/.github/workflows/publish.yml.twig
  • templates/rust/.github/workflows/stale.yml
  • templates/rust/.gitignore
  • templates/rust/CHANGELOG.md.twig
  • templates/rust/Cargo.toml.twig
  • templates/rust/LICENSE.twig
  • templates/rust/README.md.twig
  • templates/rust/examples/basic_usage.rs.twig
  • templates/rust/src/client.rs.twig
  • templates/rust/src/enums/enum.rs.twig
  • templates/rust/src/enums/mod.rs.twig
  • templates/rust/src/error.rs.twig
  • templates/rust/src/id.rs.twig
  • templates/rust/src/input_file.rs.twig
  • templates/rust/src/lib.rs.twig
  • templates/rust/src/models/mod.rs.twig
  • templates/rust/src/models/model.rs.twig
  • templates/rust/src/models/request_model.rs.twig
  • templates/rust/src/permission.rs.twig
  • templates/rust/src/query.rs.twig
  • templates/rust/src/role.rs.twig
  • templates/rust/src/services/mod.rs.twig
  • templates/rust/src/services/service.rs.twig
  • templates/rust/tests/tests.rs
  • tests/Rust183Test.php
  • tests/languages/rust/test.sh
🧰 Additional context used
🧬 Code graph analysis (1)
example.php (3)
src/SDK/Language.php (1)
  • Language (5-295)
src/SDK/Language/Rust.php (1)
  • Rust (8-696)
src/Spec/Swagger2.php (1)
  • Swagger2 (7-646)
🪛 PHPMD (2.15.0)
src/SDK/Language/Rust.php

274-274: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


408-408: Avoid unused parameters such as '$lang'. (undefined)

(UnusedFormalParameter)


539-539: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


645-645: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


646-646: Avoid unused parameters such as '$namespace'. (undefined)

(UnusedFormalParameter)


647-647: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)

🪛 Shellcheck (0.11.0)
tests/languages/rust/test.sh

[warning] 4-4: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.

(SC2164)

🔇 Additional comments (50)
templates/rust/LICENSE.twig (1)

1-1: LGTM!

The template correctly uses the raw filter to output unescaped license content.

templates/rust/.gitignore (1)

1-24: LGTM!

The .gitignore patterns are comprehensive and follow Rust ecosystem conventions. The exclusion of Cargo.lock is appropriate for library SDKs.

templates/rust/README.md.twig (1)

1-45: LGTM!

The README template is well-structured with appropriate badge URLs, installation instructions, and conditional rendering for optional sections. The use of default values for cratePackage ensures robustness.

.github/workflows/tests.yml (1)

46-46: LGTM!

The addition of Rust183 to the SDK matrix is consistent with the PR's objective to add Rust SDK support.

templates/rust/.github/workflows/stale.yml (1)

1-9: LGTM!

The workflow correctly delegates to a reusable stale workflow with an appropriate daily schedule.

pyproject.toml (1)

6-9: LGTM!

The additions to the djlint ignore list are well-justified with clear comments explaining the Rust-specific false positives (generics and references).

templates/rust/.github/workflows/autoclose.yml (1)

1-12: LGTM!

The workflow correctly uses pull_request_target to access secrets and delegates to a trusted reusable workflow. The condition appropriately allows PRs from the dev branch.

.github/workflows/sdk-build-validation.yml (3)

61-62: LGTM! Rust SDK added to the matrix.

The addition of Rust as a server-side SDK is correctly configured and follows the same pattern as other SDKs in the matrix.


203-206: LGTM! Rust build commands are appropriate.

The build and test commands follow Rust best practices:

  • cargo build --release ensures the code compiles in release mode
  • cargo test --lib runs library tests

146-148: Rust 1.83.0 version is correctly specified and aligns with project requirements.

Verification confirms that Rust 1.83.0 is a stable version released on November 28, 2024, and matches the rust-version = "1.83" specification in Cargo.toml. The workflow's pinning to this specific patch version is appropriate for deterministic builds.

templates/rust/.github/workflows/publish.yml.twig (2)

19-20: LGTM! Tests run before publishing.

Running tests before publishing is a good practice to ensure code quality.


22-23: Verify CARGO_REGISTRY_TOKEN secret is configured.

The workflow requires the CARGO_REGISTRY_TOKEN secret to publish to crates.io. Ensure this secret is properly configured in the repository settings before the first release.

example.php (2)

24-24: LGTM! Rust import added.

The import follows the same pattern as other language imports.


281-283: LGTM! Enhanced error handling with Throwable.

Catching Throwable in addition to Exception provides better error coverage for PHP 7+ (catches both exceptions and errors). This is a good defensive programming practice.

tests/Rust183Test.php (3)

7-10: LGTM! Test configuration is appropriate.

The test class configuration correctly sets up the Rust SDK test parameters with appropriate naming and platform settings.


15-15: Ensure Docker image version consistency.

The test uses rust:1.83 Docker image, which should align with:

  • The rust-version in Cargo.toml.twig (1.83)
  • The toolchain in CI workflows (1.83.0)

Verify these versions are consistent across the codebase.


14-15: The paths referenced are correct. The tests/sdks/rust/ directory is generated at test runtime by the SDK generation code in Base.php::testHTTPSuccess(), not pre-existing in the repository. The tests/languages/rust/test.sh script exists and is properly referenced. This pattern is consistent across all language tests in the codebase.

Likely an incorrect or invalid review comment.

templates/rust/Cargo.toml.twig (2)

1-18: LGTM! Package metadata is well-configured.

The package metadata section correctly uses template variables and follows Rust packaging best practices. The rust-version of 1.83 aligns with the CI toolchain version.


39-44: LGTM! Conditional test binary configuration.

The conditional inclusion of the test binary when sdk.test == "true" is a good approach that keeps the published crate clean while supporting testing during development.

templates/rust/tests/tests.rs (1)

6-22: LGTM! Main test orchestrator is well-structured.

The async main function properly orchestrates all test functions in sequence, providing clear output and proper error propagation.

templates/rust/CHANGELOG.md.twig (1)

1-23: LGTM! Changelog follows best practices.

The changelog template follows the Keep a Changelog format and provides comprehensive documentation of the initial release, including all features and capabilities.

templates/rust/examples/basic_usage.rs.twig (4)

10-20: LGTM! Client initialization example is clear and well-documented.

The example demonstrates proper client initialization with appropriate placeholder values and clear comments guiding users to replace them.


24-41: LGTM! User creation example demonstrates proper error handling.

The example shows both success and error cases with informative output, teaching users how to handle the SDK's error types correctly.


59-88: LGTM! Conditional service examples are well-structured.

The conditional inclusion of database service examples based on spec.services ensures the example remains relevant for different API specifications.


127-143: Excellent error handling demonstration.

The example explicitly demonstrates error handling with invalid credentials, showing users what to expect when API calls fail. This is valuable for learning the SDK's error patterns.

templates/rust/src/enums/mod.rs.twig (1)

1-6: LGTM!

The module aggregator correctly generates public module declarations and re-exports for all enums. The pattern follows Rust conventions.

templates/rust/src/id.rs.twig (2)

46-118: LGTM!

The test suite is comprehensive and covers length validation, uniqueness, hex format, custom IDs, padding behavior, and edge cases.


33-33: fastrand dependency is properly declared in Cargo.toml.

The fastrand = "2.0" dependency is already included in the Cargo.toml template dependencies section, so no action is needed.

templates/rust/src/permission.rs.twig (1)

58-91: LGTM!

Test coverage is solid and validates the permission string formatting for all builder methods.

templates/rust/src/lib.rs.twig (1)

1-62: LGTM!

The library facade follows Rust conventions with:

  • Clear module organization
  • Idiomatic re-exports of commonly used types
  • Type alias for SDK Result type
  • SDK metadata as public constants
  • Comprehensive documentation with usage example
templates/rust/src/services/mod.rs.twig (2)

24-33: LGTM!

The initialization order is correct: services are instantiated with references to client (lines 28-30) before client is moved into the struct (line 31). This ensures each service has access to the client configuration during construction.


10-14: LGTM!

The Service trait provides a clean abstraction for accessing the underlying client from any service implementation.

templates/rust/src/models/request_model.rs.twig (3)

1-23: LGTM!

The request model struct follows Rust best practices:

  • Proper serde attributes for JSON serialization
  • skip_serializing_if for optional fields produces clean JSON
  • Field name transformation handles various casing conventions
  • Conditional Default derive for test convenience

25-49: LGTM!

The accessor pattern is idiomatic:

  • Fluent setters for optional fields (builder pattern)
  • Reference getters for both required and optional fields
  • Consistent naming convention

51-77: LGTM!

Tests validate struct creation, accessor methods, and JSON serialization round-trip.

templates/rust/src/role.rs.twig (3)

1-66: LGTM!

The Role builder implementation is excellent:

  • Private constructor ensures controlled construction
  • Generic Into<String> parameters provide flexibility
  • Optional parameters handled idiomatically with Option<&str>
  • Consistent string formatting for all role types

68-90: LGTM!

The trait implementations provide ergonomic conversions:

  • Display for string rendering
  • From<Role> for owned string extraction
  • From<&str> and From<String> for flexible role creation

92-155: LGTM!

Comprehensive test coverage validates all constructors, optional parameters, and conversions.

templates/rust/src/enums/enum.rs.twig (2)

4-4: Note: Copy derive restricts enum variants to simple types.

The Copy derive on Line 4 means all enum variants must be Copy, which prevents adding fields to variants later. This is appropriate for simple value enums (like status codes or flags) but would need to be removed if variants require associated data.

Given this template generates API enums from specifications, the restriction is likely intentional and acceptable.


14-30: LGTM!

The as_str() method and Display implementation provide idiomatic string conversion for enum variants, with proper serde attribute mapping for serialization.

templates/rust/src/models/mod.rs.twig (1)

1-24: LGTM!

The module structure is well-organized with proper re-exports for convenient access. The Model trait with Serialize + Deserialize + Clone + Debug bounds is appropriate for SDK data models, and the blanket implementations for all generated types ensure consistency.

templates/rust/src/models/model.rs.twig (1)

1-49: LGTM on the model generation template.

The struct definition with serde attributes, optional field handling with skip_serializing_if, and builder-style setters for optional fields follow idiomatic Rust patterns. The getter methods returning references are appropriate for the use case.

templates/rust/src/services/service.rs.twig (2)

26-34: LGTM on service construction.

The service pattern with new(client: &Client) that clones the client and client() accessor is clean and follows the SDK's arc-swap architecture for mutable client state.


35-141: Well-structured method generation with proper handling of various parameter types.

The template correctly:

  • Orders required parameters before optional ones (Lines 52-66)
  • Uses Option<&str> for optional impl Into<String> types (Line 61)
  • Handles query, body, and header parameters appropriately
  • Routes to correct client methods based on method type (file upload, webAuth, location, standard)
templates/rust/src/error.rs.twig (1)

1-78: LGTM on error handling design.

The error type with thiserror::Error derive, status code classification methods (is_client_error, is_server_error), and From implementations for common error types provide a solid foundation for SDK error handling.

templates/rust/src/input_file.rs.twig (1)

1-161: LGTM on InputFile and ChunkedReader implementation.

The dual-source design (Path vs Bytes), async/sync method pairs, and chunked reading with proper seek support are well-implemented. The From trait implementations provide convenient conversion ergonomics.

Also applies to: 171-294

templates/rust/src/client.rs.twig (1)

59-112: LGTM on core client architecture.

The ArcSwap pattern for thread-safe mutable configuration, snapshot-based request execution, and the separation of concerns between single/chunked uploads are well-designed. Response handling correctly distinguishes JSON vs non-JSON content types.

Also applies to: 332-373, 566-631, 818-874

src/SDK/Language/Rust.php (2)

284-289: Fragile string matching for collection attributes.

Relying on description text like "Collection attributes" or "List of attributes" to determine type is brittle. If the API spec descriptions change, this logic will break silently.

Consider whether there's a more reliable way to identify these types (e.g., schema properties, custom annotations).


1-97: LGTM on the Rust language backend implementation.

The class provides comprehensive Rust code generation support with:

  • Complete keyword list and raw identifier overrides
  • Well-structured file scaffolding
  • Type mapping with proper handling of arrays, enums, and models
  • Useful Twig filters for rustdoc comments, type resolution, and DX improvements (impl Into patterns)

Also applies to: 139-267, 312-468, 470-531, 554-608, 610-641

templates/rust/src/query.rs.twig (1)

1-208: LGTM on the Query builder implementation.

Comprehensive coverage of query operations (comparison, sorting, pagination, geospatial) with ergonomic generic bounds (impl Into<String>, impl Into<Value>). The test suite thoroughly validates serialization and edge cases.

Also applies to: 234-377

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @src/SDK/Language/Rust.php:
- Around line 642-666: The getReturnType method declares parameters $spec,
$namespace, and $generic that are never used; update getReturnType to either
remove these parameters if they are truly unnecessary or explicitly mark them as
intentionally unused for clarity and to silence static-analysis warnings: add a
docblock above getReturnType explaining that $spec, $namespace, and $generic are
reserved for interface compatibility/future use and include @param tags for
each, and/or add a one-line intentional-unused suppression (or a no-op
reference) for $spec, $namespace, and $generic inside getReturnType so tools and
readers know they are intentionally unused.
- Around line 538-551: The parameter $generic in getPropertyType is declared but
unused; mark it as intentionally unused and document why: add a short inline
comment stating it's retained for signature consistency with the Twig filter and
then consume it to avoid unused-variable warnings (e.g., cast to void or rename
to $_generic). Update the getPropertyType function header accordingly so
tools/readers know the parameter is intentionally unused while keeping the
signature compatible with the Twig filter.
- Around line 407-467: The method getParamExample declares an unused $lang
parameter; either remove $lang from the method signature and update any callers
(or the parent/interface if applicable), or if the parameter must remain for
interface compatibility, mark it as intentionally unused by renaming to $_lang
or adding a brief comment inside getParamExample explaining it is deliberately
unused for future/compatibility reasons so linters and readers understand why it
exists; ensure any changes reference the getParamExample method to locate the
change.
- Around line 38-136: getKeywords() is missing the Rust keywords "gen", "try",
and the case-sensitive "Self"; add "gen", "try", and "Self" to the array
returned by getKeywords() in Rust.php, and update getIdentifierOverrides() to
include the mappings "gen" => "r#gen", "try" => "r#try", and "Self" => "r#Self"
so identifiers that collide with these reserved/strict keywords are properly
escaped; edit the arrays returned by the methods getKeywords() and
getIdentifierOverrides() to include these entries.
🧹 Nitpick comments (2)
src/SDK/Language/Rust.php (2)

301-305: Consider simplifying nested array type logic.

The triple-nested ternary operators are difficult to read and maintain. Consider extracting this logic into a helper method or using early returns for better clarity.

♻️ Example refactor
self::TYPE_ARRAY => $this->getArrayTypeName($parameter),

Add a helper method:

private function getArrayTypeName(array $parameter): string
{
    if (isset($parameter["array"]["model"])) {
        return "Vec<crate::models::" . ucfirst($parameter["array"]["model"]) . ">";
    }
    
    if (!empty($parameter["array"]["type"]) && !is_array($parameter["array"]["type"])) {
        return "Vec<" . $this->getTypeName($parameter["array"]) . ">";
    }
    
    return "Vec<String>";
}

475-487: Consider wordwrap behavior in code examples.

The rustdocComment filter wraps text at 75 characters. This may break code examples or formatted content within documentation comments. Consider detecting and preserving code blocks or preformatted sections.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5ade7f and 2672b2c.

📒 Files selected for processing (4)
  • src/SDK/Language/Rust.php
  • templates/rust/Cargo.toml.twig
  • templates/rust/README.md.twig
  • tests/languages/rust/test.sh
✅ Files skipped from review due to trivial changes (1)
  • templates/rust/README.md.twig
🚧 Files skipped from review as they are similar to previous changes (2)
  • templates/rust/Cargo.toml.twig
  • tests/languages/rust/test.sh
🧰 Additional context used
🪛 PHPMD (2.15.0)
src/SDK/Language/Rust.php

273-273: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


407-407: Avoid unused parameters such as '$lang'. (undefined)

(UnusedFormalParameter)


538-538: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


644-644: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


645-645: Avoid unused parameters such as '$namespace'. (undefined)

(UnusedFormalParameter)


646-646: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, WebNode)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Ruby27)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Python310)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, DotNet60)
  • GitHub Check: build (8.3, CLINode18)
  • GitHub Check: swift (server)
  • GitHub Check: rust (server)
  • GitHub Check: apple (client)
  • GitHub Check: android (client)
🔇 Additional comments (6)
src/SDK/Language/Rust.php (6)

1-31: LGTM: Class structure and basic setup.

The class structure, namespace, imports, and fluent setter pattern are well-implemented and follow PHP best practices.


141-266: LGTM: Comprehensive file generation configuration.

The file generation array covers all essential components of a Rust crate: package manifest, source modules, tests, documentation, and CI/CD workflows. The structure follows Rust conventions.


315-335: LGTM: Rust syntax helpers are correct.

The helper methods return the correct Rust syntax: :: for static access, " for string quotes, and vec![...] for array literals.


561-607: LGTM: Excellent use of Rust ergonomic patterns.

The use of impl Into<String> and impl IntoIterator<Item = impl Into<String>> for input parameters is a best practice in Rust that significantly improves developer experience by accepting various input types (owned, borrowed, slices, etc.).


617-633: LGTM: Parameter value conversions match input types.

The .into() and .into_iter().map().collect() conversions correctly align with the impl Into and impl IntoIterator input types from getInputType, ensuring type safety.


673-694: LGTM: Recursive generic type checking.

The hasGenericType method correctly implements recursive traversal through model definitions to detect generic types, checking both additionalProperties and nested sub_schema references.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @src/SDK/Language/Rust.php:
- Around line 696-717: The hasGenericType method accesses
$spec["definitions"][$model], $model["additionalProperties"], and iterates
$model["properties"] without null/isset checks; update hasGenericType to first
verify that $model is a key in $spec["definitions"] (use isset or
array_key_exists) and return false if missing, then check that the resolved
$model is an array and that "additionalProperties" exists before evaluating it,
and ensure "properties" exists and is an array before looping and checking each
property's "sub_schema" to avoid undefined index/null errors when recursing.

In @templates/rust/src/client.rs.twig:
- Around line 15-16: The DEFAULT_TIMEOUT constant (const DEFAULT_TIMEOUT: u64 =
10) is too short for file uploads; either increase the default or add a separate
upload-specific timeout and use it for upload requests. Change the constant
strategy by adding a new UPLOAD_TIMEOUT (e.g., 60s) or bump DEFAULT_TIMEOUT,
then update the client code paths that perform file upload operations to use the
new upload timeout (references: DEFAULT_TIMEOUT constant and the upload request
function/method in client.rs.twig) and ensure any request builder or HTTP client
initialization uses the appropriate timeout for uploads.
🧹 Nitpick comments (7)
templates/rust/src/models/model.rs.twig (2)

7-7: Unused template variable fieldNames.

The variable fieldNames is declared but never used in this template. Consider removing it to reduce template clutter.

Proposed fix
-{% set fieldNames = {} %}
-
 {{ definition.description | rustdocComment }}

33-37: Consider extracting field name conversion to a Twig macro.

The field name conversion logic is duplicated between the struct definition (lines 14-16) and the impl block (lines 35-37). If this logic needs to change, both locations must be updated in sync. Consider extracting this to a reusable Twig macro or filter to ensure consistency.

templates/rust/src/client.rs.twig (2)

125-136: Consider returning Result instead of panicking on invalid endpoint.

Panicking in library code is generally discouraged as it removes control from the caller. Consider returning a Result<Self, Error> to allow callers to handle invalid endpoints gracefully.

Alternative approach
/// Set the API endpoint
pub fn set_endpoint<S: Into<String>>(&self, endpoint: S) -> Result<Self> {
    let endpoint = endpoint.into();
    if !endpoint.starts_with("http://") && !endpoint.starts_with("https://") {
        return Err({{ spec.title | caseUcfirst }}Error::new(
            0,
            format!("Invalid endpoint URL: {}. Endpoint must start with http:// or https://", endpoint),
            None,
            String::new(),
        ));
    }
    // ... rest of implementation
    Ok(self.clone())
}

217-234: Silent failure when adding invalid headers could cause debugging difficulties.

The add_header method silently ignores invalid header names or values (lines 225-231). Consider logging a warning or returning a Result to inform callers when headers cannot be added.

src/SDK/Language/Rust.php (3)

279-318: The $spec parameter is never used in getTypeName.

While $spec is passed to the method and appears in the signature for API consistency, it's never actually used in the method body. The recursive call at line 312 also doesn't pass $spec. If this is intentional for interface compatibility, consider adding a comment to clarify.

Additionally, the complex ternary expression at lines 309-313 could be simplified for readability.

Simplified array handling
self::TYPE_ARRAY => $this->getArrayTypeName($parameter),

With a separate helper method:

private function getArrayTypeName(array $parameter): string
{
    if (isset($parameter["array"]["model"])) {
        return "Vec<crate::models::" . ucfirst($parameter["array"]["model"]) . ">";
    }
    
    $arrayItems = $parameter["array"] ?? [];
    if (!empty($arrayItems["type"]) && !is_array($arrayItems["type"])) {
        return "Vec<" . $this->getTypeName($arrayItems) . ">";
    }
    
    return "Vec<String>";
}

650-679: Several parameters in getReturnType are unused.

The parameters $spec, $namespace, and $generic are never used in this method. If they're required for interface consistency with other language implementations, consider adding @SuppressWarnings(PHPMD.UnusedFormalParameter) or a comment explaining why they're present.

Add suppression annotation
/**
 * @param array $method
 * @param array $spec
 * @param string $namespace
 * @param string $generic
 * @return string
 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
 */
protected function getReturnType(

681-689: Unused $response variable in foreach loop.

The $response variable is never used; only the $code key is needed.

Proposed fix
 protected function isEmptyResponse(array $responses): bool
 {
-    foreach ($responses as $code => $response) {
+    foreach (array_keys($responses) as $code) {
         if (!in_array((int)$code, [204, 205])) {
             return false;
         }
     }
     return !empty($responses);
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2672b2c and 530125d.

📒 Files selected for processing (3)
  • src/SDK/Language/Rust.php
  • templates/rust/src/client.rs.twig
  • templates/rust/src/models/model.rs.twig
🧰 Additional context used
🧬 Code graph analysis (1)
src/SDK/Language/Rust.php (1)
src/SDK/Language.php (2)
  • Language (5-295)
  • toPascalCase (116-119)
🪛 PHPMD (2.15.0)
src/SDK/Language/Rust.php

279-279: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


415-415: Avoid unused parameters such as '$lang'. (undefined)

(UnusedFormalParameter)


546-546: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


652-652: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


653-653: Avoid unused parameters such as '$namespace'. (undefined)

(UnusedFormalParameter)


654-654: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


683-683: Avoid unused local variables such as '$response'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: react-native (client)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: build (8.3, PHP80)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, Python313)
  • GitHub Check: build (8.3, Ruby30)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: build (8.3, Rust183)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, CLIBun11)
  • GitHub Check: dotnet (server)
  • GitHub Check: rust (server)
  • GitHub Check: apple (client)
  • GitHub Check: swift (server)
  • GitHub Check: android (client)
🔇 Additional comments (8)
templates/rust/src/models/model.rs.twig (1)

70-96: LGTM!

The test module provides basic validation for model creation and serialization round-trips. The use of #[cfg_attr(test, derive(Default))] is a good approach to enable testing without requiring Default in production code.

templates/rust/src/client.rs.twig (4)

369-470: LGTM with minor observation on duplicated header logic.

The call_location method correctly handles redirect responses for webAuth endpoints. The header application logic is duplicated (lines 389-395 and 407-412) when handling GET requests with params, but this is functionally correct.


676-802: LGTM!

The chunked file upload implementation with resumable upload support is well-structured. The use of Content-Range headers and the ability to resume from a specific chunk offset is correctly implemented. The defensive check for empty chunks at lines 726-728 adds robustness.


826-830: Empty response handling may fail for types that don't deserialize from null.

When the response body is empty (line 826-827), deserializing b"null" works for () and Option<T> but will fail for types that don't accept JSON null. This could cause confusing errors when the API returns an empty 200 response but the caller expects a concrete type.

Consider documenting this behavior or returning a more descriptive error when deserialization of null fails.


863-882: LGTM!

The tests adequately cover client creation and the builder pattern configuration.

src/SDK/Language/Rust.php (3)

38-142: LGTM!

The Rust keywords list and identifier overrides are comprehensive. The use of r# prefix for raw identifiers is the correct Rust approach for escaping reserved words.


383-405: Object and array $default values are ignored.

When a non-empty $default is provided for TYPE_OBJECT or TYPE_ARRAY (lines 395-400), the actual default value is ignored and serde_json::Value::Null or Vec::new() is returned instead. If API specs define meaningful object/array defaults, they won't be reflected in the generated code.

Verify if this is intentional behavior or if object/array defaults need to be serialized to Rust syntax.


480-538: LGTM!

The Twig filters are well-designed for Rust code generation. The rustdocComment filter correctly handles multi-line documentation, and the rustType filter properly unescapes HTML entities that Twig may introduce.

fix: uploadId.
add: tests.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @src/SDK/Language/Rust.php:
- Around line 701-722: The hasGenericType method accesses
$model["additionalProperties"] directly which can be undefined; update
hasGenericType to guard that access by checking array_key_exists or using null
coalescing (e.g., treat missing additionalProperties as false) before evaluating
it, so replace direct $model["additionalProperties"] usage in hasGenericType
with a safe check (for example $model['additionalProperties'] ?? false) and keep
the existing logic otherwise.
- Around line 464-471: The current manual substr-based bracket stripping in the
TYPE_ARRAY case is brittle for nested or malformed arrays; replace lines that
check str_starts_with/str_ends_with and substr with a single regex match that
removes only one outermost bracket pair: use preg_match('/^\[(.*)\]$/s',
$example, $m) and if it matches set $example = $m[1]; keep the rest that builds
$output ("vec![" . $example . "]").
- Around line 38-142: The getKeywords() array in Rust.php contains
invalid/non-existent Rust keywords (alignof, offsetof, proc, pure, sizeof);
remove these five entries from the return array in the getKeywords() method and
ensure getIdentifierOverrides() does not reference any of them (update
getIdentifierOverrides() if necessary); keep all other keywords intact and
return the cleaned list from getKeywords().
🧹 Nitpick comments (6)
templates/rust/src/query.rs.twig (3)

210-232: Consider accepting Query objects directly instead of JSON strings.

The current API requires users to serialize queries to strings and then deserialize them back:

let query_strings = vec![
    Query::equal("released", true).to_string(),
    Query::less_than("releasedYear", 1990).to_string(),
];
let query = Query::or(query_strings)?;

This round-trip through JSON is inefficient and error-prone. Consider accepting Query objects directly:

♻️ Proposed alternative API
-    pub fn or<I, S>(queries: I) -> crate::error::Result<Self>
-    where
-        I: IntoIterator<Item = S>,
-        S: AsRef<str>,
-    {
-        let values: Vec<Value> = queries
-            .into_iter()
-            .map(|query| serde_json::from_str(query.as_ref()))
-            .collect::<Result<Vec<_>, _>>()?;
-        Ok(Self::new("or".to_string(), None, values))
-    }
+    pub fn or<I>(queries: I) -> Self
+    where
+        I: IntoIterator<Item = Query>,
+    {
+        let values: Vec<Value> = queries
+            .into_iter()
+            .map(|q| q.to_value())
+            .collect();
+        Self::new("or".to_string(), None, values)
+    }

This allows simpler usage:

let query = Query::or(vec![
    Query::equal("released", true),
    Query::less_than("releasedYear", 1990),
]);

If backward compatibility with string inputs is needed, consider providing both APIs (e.g., or and or_from_strings).


251-257: Minor efficiency concern in Display implementation.

The to_value() method consumes self, requiring a clone in Display::fmt. This is fine for correctness but adds an allocation on every display call.

♻️ Optional: Add a non-consuming to_value_ref method

If display performance matters, consider adding a method that borrows:

fn to_value_ref(&self) -> Value {
    let mut obj = serde_json::Map::new();
    obj.insert("method".to_string(), Value::String(self.method.clone()));

    if let Some(attr) = &self.attribute {
        obj.insert("attribute".to_string(), Value::String(attr.clone()));
    }

    if !self.values.is_empty() {
        obj.insert("values".to_string(), Value::Array(self.values.clone()));
    }

    Value::Object(obj)
}

impl std::fmt::Display for Query {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let json_str = serde_json::to_string(&self.to_value_ref())
            .unwrap_or_else(|_| String::new());
        write!(f, "{}", json_str)
    }
}

6-11: Consider deriving Serialize for simpler JSON conversion.

The Query struct manually implements to_value() and Display. Deriving Serialize from serde could simplify this and ensure consistency.

♻️ Optional: Use serde derive
+use serde::Serialize;
+
 /// Query builder for filtering and sorting database queries
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Serialize)]
+#[serde(rename_all = "camelCase")]
 pub struct Query {
     method: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
     attribute: Option<String>,
+    #[serde(skip_serializing_if = "Vec::is_empty")]
     values: Vec<Value>,
 }

Then to_value() becomes:

pub fn to_value(self) -> Value {
    serde_json::to_value(&self).unwrap()
}

This is optional since the current implementation works correctly and matches the explicit format requirements.

src/SDK/Language/Rust.php (3)

284-323: Use toPascalCase() for consistent naming convention.

Lines 288, 291, 315, and 320 use ucfirst() to capitalize type names. For consistency with other language generators in this codebase and to handle multi-word identifiers properly, consider using the inherited toPascalCase() method instead.

♻️ Proposed refactor for consistent naming
     // Handle enum types
     if (isset($parameter["enumName"])) {
-        return "crate::enums::" . ucfirst($parameter["enumName"]);
+        return "crate::enums::" . $this->toPascalCase($parameter["enumName"]);
     }
     if (!empty($parameter["enumValues"])) {
-        return "crate::enums::" . ucfirst($parameter["name"]);
+        return "crate::enums::" . $this->toPascalCase($parameter["name"]);
     }
     
     // ... later in the match expression ...
     
     self::TYPE_ARRAY => isset($parameter["array"]["model"])
-        ? "Vec<crate::models::" . ucfirst($parameter["array"]["model"]) . ">"
+        ? "Vec<crate::models::" . $this->toPascalCase($parameter["array"]["model"]) . ">"
         : (!empty(($parameter["array"] ?? [])["type"]) && !\is_array($parameter["array"]["type"])
             ? "Vec<" . $this->getTypeName($parameter["array"]) . ">"
             : "Vec<String>"),
     default => isset($parameter["model"])
-        ? "crate::models::" . ucfirst($parameter["model"])
+        ? "crate::models::" . $this->toPascalCase($parameter["model"])
         : $parameter["type"],

354-413: Consider consolidating duplicate switch logic.

Lines 367-387 and 389-410 contain nearly identical switch statements with only minor differences in output values. This duplication makes maintenance harder.

♻️ Proposed refactor to reduce duplication
 public function getParamDefault(array $param): string
 {
     $type = $param["type"] ?? "";
     $default = $param["default"] ?? "";
     $required = $param["required"] ?? "";
 
     if ($required) {
         return "";
     }
 
     $output = " = ";
+    $hasExplicitDefault = !empty($default) || $default === 0 || $default === false;
 
-    if (empty($default) && $default !== 0 && $default !== false) {
-        switch ($type) {
-            case self::TYPE_NUMBER:
-            case self::TYPE_INTEGER:
-                $output .= "0";
-                break;
-            case self::TYPE_BOOLEAN:
-                $output .= "false";
-                break;
-            case self::TYPE_STRING:
-                $output .= "String::new()";
-                break;
-            case self::TYPE_OBJECT:
-                $output .= "serde_json::Value::Null";
-                break;
-            case self::TYPE_ARRAY:
-                $output .= "Vec::new()";
-                break;
-            case self::TYPE_FILE:
-                $output .= "InputFile::default()";
-                break;
-        }
-    } else {
-        switch ($type) {
-            case self::TYPE_NUMBER:
-            case self::TYPE_INTEGER:
-                $output .= $default;
-                break;
-            case self::TYPE_BOOLEAN:
-                $output .= $default ? "true" : "false";
-                break;
-            case self::TYPE_STRING:
-                $output .= "String::from(\"$default\")";
-                break;
-            case self::TYPE_OBJECT:
-                $output .= "serde_json::Value::Null";
-                break;
-            case self::TYPE_ARRAY:
-                $output .= "Vec::new()";
-                break;
-            case self::TYPE_FILE:
-                $output .= "InputFile::default()";
-                break;
-        }
-    }
+    switch ($type) {
+        case self::TYPE_NUMBER:
+        case self::TYPE_INTEGER:
+            $output .= $hasExplicitDefault ? $default : "0";
+            break;
+        case self::TYPE_BOOLEAN:
+            $output .= ($hasExplicitDefault && $default) ? "true" : "false";
+            break;
+        case self::TYPE_STRING:
+            $output .= $hasExplicitDefault ? "String::from(\"$default\")" : "String::new()";
+            break;
+        case self::TYPE_OBJECT:
+            $output .= "serde_json::Value::Null";
+            break;
+        case self::TYPE_ARRAY:
+            $output .= "Vec::new()";
+            break;
+        case self::TYPE_FILE:
+            $output .= "InputFile::default()";
+            break;
+    }
 
     return $output;
 }

686-694: Unused loop variable in isEmptyResponse.

Line 688: The $response variable in the foreach loop is never used. You can replace it with an underscore or use array_keys() if only codes are needed.

♻️ Proposed fix
 protected function isEmptyResponse(array $responses): bool
 {
-    foreach ($responses as $code => $response) {
+    foreach ($responses as $code => $_) {
         if (!in_array((int)$code, [204, 205])) {
             return false;
         }
     }
     return !empty($responses);
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 530125d and d4502f8.

📒 Files selected for processing (4)
  • src/SDK/Language/Rust.php
  • templates/rust/src/lib.rs.twig
  • templates/rust/src/query.rs.twig
  • templates/rust/src/services/service.rs.twig
🚧 Files skipped from review as they are similar to previous changes (1)
  • templates/rust/src/services/service.rs.twig
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-25T12:47:16.427Z
Learnt from: ItzNotABug
Repo: appwrite/sdk-generator PR: 1273
File: src/Spec/Swagger2.php:167-179
Timestamp: 2025-12-25T12:47:16.427Z
Learning: In src/Spec/Swagger2.php, when parsing `x-oneOf` entries from OpenAPI specs, the `$ref` key is always guaranteed to be present by design. If it's missing, it indicates a broader spec validation issue rather than a normal edge case to handle.

Applied to files:

  • src/SDK/Language/Rust.php
🧬 Code graph analysis (1)
src/SDK/Language/Rust.php (1)
src/SDK/Language.php (2)
  • Language (5-295)
  • toPascalCase (116-119)
🪛 PHPMD (2.15.0)
src/SDK/Language/Rust.php

284-284: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


420-420: Avoid unused parameters such as '$lang'. (undefined)

(UnusedFormalParameter)


551-551: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


657-657: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


658-658: Avoid unused parameters such as '$namespace'. (undefined)

(UnusedFormalParameter)


659-659: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


688-688: Avoid unused local variables such as '$response'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, Python311)
  • GitHub Check: build (8.3, Ruby27)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: dotnet (server)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: react-native (client)
  • GitHub Check: android (client)
  • GitHub Check: flutter (client)
  • GitHub Check: apple (client)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, Android14Java17)
🔇 Additional comments (10)
templates/rust/src/lib.rs.twig (1)

1-63: LGTM! Clean library structure with good documentation.

The module organization follows Rust conventions with clear separation between core functionality and utility modules. The re-exports, Result type alias, and SDK constants are well-designed.

One minor observation: the usage example at lines 21-24 shows set_* methods returning Client, implying a builder pattern. Ensure these methods in client.rs.twig actually return Self for method chaining to work as documented.

templates/rust/src/query.rs.twig (1)

259-654: Excellent test coverage.

The test suite is comprehensive, covering:

  • All query operators (equal, not_equal, comparison operators, etc.)
  • Pagination methods (limit, offset, cursor_*)
  • Spatial/geometry queries
  • Composite queries (or/and) including error cases
  • JSON serialization round-trip
src/SDK/Language/Rust.php (8)

1-31: LGTM: Clean class structure and configuration.

The class declaration, namespace imports, and basic configuration methods follow proper PHP conventions and the parent Language abstract class pattern.


147-277: LGTM: Comprehensive Rust crate scaffolding.

The file structure follows idiomatic Rust conventions with proper module organization (src/lib.rs, src/models/mod.rs, src/services/mod.rs), test structure (tests/tests.rs), and CI workflows. The template scoping (default, copy, service, definition, etc.) provides good flexibility for code generation.


325-348: LGTM: Rust-specific syntax helpers.

The utility methods correctly return Rust-specific syntax: :: for static access, " for string quotes, and vec![...] for arrays.


485-543: LGTM: Well-designed Twig filters for Rust codegen.

The filters cover essential transformations:

  • rustdocComment: Properly formats documentation with /// prefix and word wrapping
  • propertyType, inputType, returnType: Delegate to type resolution helpers
  • caseEnumKey: Correctly uses toPascalCase() for enum variants
  • rustType: Unescapes HTML entities for angle brackets in generics
  • rustCrateName: Converts hyphens to underscores per Rust naming conventions
  • stripProtocol: Utility for cleaning URLs

551-620: LGTM: Excellent use of impl Into<T> for ergonomic APIs.

The getInputType method demonstrates good Rust API design by using:

  • impl Into<String> to accept &str, String, Cow<str>, etc.
  • impl IntoIterator<Item = impl Into<String>> to accept slices, arrays, and vectors

This significantly improves the developer experience while maintaining type safety. The careful checks to avoid wrapping enums, models, and primitives in Into traits show attention to detail.


622-646: LGTM: Proper parameter value conversions.

The getParamValue method correctly applies necessary transformations:

  • .into() for String parameters accepting impl Into<String>
  • .into_iter().map(|s| s.into()).collect() for Vec<String> parameters accepting iterators

This pairs well with the getInputType method's trait bounds.


655-684: LGTM: Comprehensive return type handling.

The getReturnType method correctly handles various response scenarios:

  • webAuthResult<String> for OAuth flows
  • locationResult<Vec<u8>> for binary/redirect responses
  • Empty responses (204/205) → Result<()>
  • Generic/unknown responses → Result<serde_json::Value>
  • Typed responses → Result<crate::models::ModelName>

686-694: LGTM: Correct empty response detection.

The isEmptyResponse method correctly identifies HTTP 204 (No Content) and 205 (Reset Content) status codes as empty responses. The logic ensures at least one response is present before returning true.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @templates/rust/src/operator.rs.twig:
- Around line 47-156: validate_numeric currently misses Value::Null and
non-numeric JSON (strings/booleans) and therefore lets NaN/Infinity or
non-numbers through; update validate_numeric to first assert value.is_number()
(panic with param_name if false), then use as_f64() to check for is_nan() or
is_infinite(); extract the repeated "divisor != 0" check into a small helper
(e.g., validate_nonzero_divisor) that accepts &Value and a name and checks
number-ness then tests as_f64()/as_i64() for zero, and call that helper from
divide, divide_with_min and modulo; ensure all numeric operator builders
(increment_by, increment_with_max, decrement_by, decrement_with_min, multiply,
multiply_with_max, etc.) call the tightened validate_numeric so non-numeric
Value::Null or strings/booleans are rejected consistently.
🧹 Nitpick comments (2)
templates/rust/src/operator.rs.twig (2)

33-45: Consider avoiding method: String allocation and/or returning Result<String, _> from parse_operator.
serde_json::to_string here should be effectively infallible (given Value), but the API currently commits you to panics and repeated allocations.


257-412: Add tests for NaN/Infinity + non-number inputs, and cover divide_with_min(0, ...).
The zero-panic tests are good; extending coverage here will lock in the numeric contract.

Example test additions
     #[test]
     #[should_panic(expected = "divisor cannot be zero")]
     fn test_divide_zero() {
         divide(0);
     }

+    #[test]
+    #[should_panic(expected = "divisor cannot be zero")]
+    fn test_divide_with_min_zero() {
+        divide_with_min(0, 1);
+    }
+
     #[test]
     #[should_panic(expected = "divisor cannot be zero")]
     fn test_modulo_zero() {
         modulo(0);
     }
+
+    #[test]
+    #[should_panic(expected = "value must be a finite number")]
+    fn test_increment_by_nan() {
+        increment_by(f64::NAN);
+    }
+
+    #[test]
+    #[should_panic(expected = "value must be a finite number")]
+    fn test_increment_by_non_number() {
+        increment_by("not-a-number");
+    }
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4502f8 and b5938a6.

📒 Files selected for processing (3)
  • .gitignore
  • templates/rust/src/operator.rs.twig
  • templates/rust/src/query.rs.twig
🚧 Files skipped from review as they are similar to previous changes (1)
  • templates/rust/src/query.rs.twig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: build (8.3, PHP83)
  • GitHub Check: build (8.3, DotNet80)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, CLIBun13)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: rust (server)
  • GitHub Check: android (client)
  • GitHub Check: swift (server)
  • GitHub Check: react-native (client)
  • GitHub Check: kotlin (server)
  • GitHub Check: apple (client)
🔇 Additional comments (2)
.gitignore (1)

28-28: LGTM!

The addition of the demos directory to .gitignore aligns well with the PR's goal of generating example and demo code while keeping version control clean. This mirrors the existing examples/* exclusion and makes sense for a Rust SDK generation feature.

templates/rust/src/operator.rs.twig (1)

172-229: Wire format for array operators is correct. The current implementation matches the canonical behavior across SDKs (Node.js, etc.). Array operators intentionally flatten slice values into the values array as {"method":"arrayAppend","values":[a,b]}, not {"method":"arrayAppend","values":[[a,b]]}. No changes needed.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @templates/rust/tests/tests.rs:
- Line 131: Remove the misleading standalone println!("Invalid endpoint URL:
htp://cloud.appwrite.io/v1") in templates/rust/tests/tests.rs or replace it with
a real test: create a test that constructs the client/request using the invalid
URL and asserts the operation returns an Err (e.g., assert!(result.is_err()) or
matches the expected error), ensuring the test exercises the URL validation path
rather than only printing a message.
🧹 Nitpick comments (5)
templates/rust/tests/tests.rs (5)

98-101: Clarify the intent of duplicate upload calls.

The same upload operations are called twice consecutively. If this is intentional (e.g., testing idempotency), consider adding a comment to clarify. Otherwise, these appear to be unintentional duplicates.


107-129: Error test cases don't fail when errors are not returned.

The error tests for 400, 500, and 502 responses expect errors and handle them correctly. However, the Ok(_) => {} branches mean if these endpoints unexpectedly succeed, the test passes silently without indicating the unexpected behavior.

Consider adding assertions or print statements in the Ok branch to flag unexpected success:

Proposed improvement
     match general.error400().await {
-        Ok(_) => {},
+        Ok(_) => eprintln!("Expected error400 to fail, but it succeeded"),
         Err(e) => {
             println!("{}", e.message);
             println!("{}", e.response);
         },
     }

133-133: Explicitly ignoring result may hide errors.

The let _ = general.empty().await; statement explicitly discards the result, potentially hiding errors. If empty() is expected to succeed, consider using ? to propagate errors. If it's expected to be a no-op regardless of outcome, add a comment explaining why the result is ignored.


153-201: Hardcoded absolute paths reduce portability.

Lines 155 and 188 use hardcoded absolute paths (/app/tests/resources/file.png and /app/tests/resources/large_file.mp4) that assume a specific directory structure (likely Docker container layout). This reduces portability and may cause tests to fail in different environments.

Consider:

  1. Making paths configurable via environment variables
  2. Using relative paths from the project root
  3. Using env!("CARGO_MANIFEST_DIR") to construct paths relative to the crate root
Example using CARGO_MANIFEST_DIR
use std::env;

let project_root = env!("CARGO_MANIFEST_DIR");
let upload_file = Path::new(project_root).join("tests/resources/file.png");

238-245: Duplicate query tests with identical parameters.

Lines 238-245 contain what appear to be duplicate test cases:

  • Lines 238-239: Both test distance_equal with the same parameters
  • Lines 240-241: Both test distance_not_equal with the same parameters
  • Lines 242-243: Both test distance_greater_than with the same parameters
  • Lines 244-245: Both test distance_less_than with the same parameters

If these duplicates are intentional (e.g., testing different internal representations), consider adding comments to clarify. Otherwise, remove the redundant calls.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5938a6 and 7b72140.

📒 Files selected for processing (1)
  • templates/rust/tests/tests.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: swift (server)
  • GitHub Check: apple (client)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, Python312)
  • GitHub Check: build (8.3, Python39)
  • GitHub Check: build (8.3, Python313)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: build (8.3, PHP83)
  • GitHub Check: build (8.3, Python310)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Rust183)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, DotNet60)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: build (8.3, FlutterBeta)
🔇 Additional comments (1)
templates/rust/tests/tests.rs (1)

6-22: LGTM!

The test harness setup is well-structured with proper async runtime configuration and client initialization for mock testing.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/SDK/Language/Rust.php`:
- Around line 311-313: The Vec<> branch constructs the inner type via a
recursive call to getTypeName but omits the $spec context, which can break
model/type resolution; update the call in the array-handling branch (where it
currently calls $this->getTypeName($parameter["array"])) to pass the spec as the
second argument (e.g., $this->getTypeName($parameter["array"], $spec)) so nested
array element types are resolved with the same spec context.
♻️ Duplicate comments (1)
src/SDK/Language/Rust.php (1)

459-466: Bracket stripping for array examples could be simplified.

The current approach manually strips brackets which is fragile for edge cases. Consider using trim($example, '[]') for cleaner handling.

🧹 Nitpick comments (4)
src/SDK/Language/Rust.php (1)

688-696: Unused loop variable $response.

The $response variable in the foreach loop is never used; only $code is checked. Consider using array_keys() or renaming to $_ to indicate intentional disuse.

♻️ Proposed fix
     protected function isEmptyResponse(array $responses): bool
     {
-        foreach ($responses as $code => $response) {
+        foreach (array_keys($responses) as $code) {
             if (!in_array((int)$code, [204, 205])) {
                 return false;
             }
         }
         return !empty($responses);
     }
templates/rust/src/client.rs.twig (3)

127-131: Consider returning Result instead of panicking on invalid endpoint.

Using panic!() in library code is generally discouraged as it prevents callers from handling the error gracefully. Consider returning a Result type to allow error recovery.

♻️ Alternative approach
pub fn set_endpoint<S: Into<String>>(&self, endpoint: S) -> Result<Self> {
    let endpoint = endpoint.into();
    if !endpoint.starts_with("http://") && !endpoint.starts_with("https://") {
        return Err({{ spec.title | caseUcfirst }}Error::new(
            0,
            format!("Invalid endpoint URL: {}. Endpoint must start with http:// or https://", endpoint),
            None,
            String::new(),
        ));
    }
    // ... rest of implementation
    Ok(self.clone())
}

238-248: Silent failure when adding invalid headers.

The add_header method silently ignores invalid header names or values. This could make debugging difficult for users who don't realize their custom headers weren't applied. Consider logging a warning or returning a Result.


402-431: Duplicated header application logic.

The custom header application logic is duplicated between lines 406-410 and 423-427. When GET params are present, the request is rebuilt and headers must be reapplied. Consider extracting header application to a helper or restructuring the code to avoid duplication.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b72140 and cf9f274.

📒 Files selected for processing (6)
  • .github/workflows/sdk-build-validation.yml
  • .github/workflows/tests.yml
  • example.php
  • src/SDK/Language/Rust.php
  • templates/rust/src/client.rs.twig
  • templates/rust/src/error.rs.twig
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/sdk-build-validation.yml
  • example.php
  • templates/rust/src/error.rs.twig
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-25T12:47:16.427Z
Learnt from: ItzNotABug
Repo: appwrite/sdk-generator PR: 1273
File: src/Spec/Swagger2.php:167-179
Timestamp: 2025-12-25T12:47:16.427Z
Learning: In src/Spec/Swagger2.php, when parsing `x-oneOf` entries from OpenAPI specs, the `$ref` key is always guaranteed to be present by design. If it's missing, it indicates a broader spec validation issue rather than a normal edge case to handle.

Applied to files:

  • src/SDK/Language/Rust.php
🧬 Code graph analysis (1)
src/SDK/Language/Rust.php (1)
src/SDK/Language.php (1)
  • Language (5-295)
🪛 PHPMD (2.15.0)
src/SDK/Language/Rust.php

279-279: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


415-415: Avoid unused parameters such as '$lang'. (undefined)

(UnusedFormalParameter)


546-546: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


652-652: Avoid unused parameters such as '$spec'. (undefined)

(UnusedFormalParameter)


653-653: Avoid unused parameters such as '$namespace'. (undefined)

(UnusedFormalParameter)


654-654: Avoid unused parameters such as '$generic'. (undefined)

(UnusedFormalParameter)


690-690: Avoid unused local variables such as '$response'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: rust (server)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, Python312)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, Node16)
  • GitHub Check: build (8.3, Ruby30)
  • GitHub Check: build (8.3, Rust183)
  • GitHub Check: cli (console)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: build (8.3, Python313)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: build (8.3, WebNode)
  • GitHub Check: swift (server)
  • GitHub Check: flutter (client)
  • GitHub Check: build (8.3, Ruby27)
  • GitHub Check: android (client)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: apple (client)
🔇 Additional comments (2)
.github/workflows/tests.yml (1)

47-47: LGTM!

The addition of Rust183 to the CI matrix is consistent with the naming convention of other SDKs and properly integrates the new Rust SDK tests into the workflow.

templates/rust/src/client.rs.twig (1)

878-897: LGTM!

The test coverage for client creation and builder pattern usage provides a good baseline. The implementation correctly validates the endpoint configuration and exercises the builder methods.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants