Repository: apache/opendal (language: rust)

Issue:
bug: When using gdrive as the schema, `create_dir` is not working properly
### Describe the bug

When calling `create_dir` with gdrive as the schema, the directory is not created with an expected name.
I was using the latest Node.js bindings.

### Steps to Reproduce

```js
import { Operator } from 'opendal'

const op = new Operator('gdrive', { access_token: '****' })

// a directory with name 'a/' created since Google Drive treats '/' as a valid character for file name
await op.createDir('a/')

// an error occurred:
// Error: NotADirectory (permanent) at create_dir, context: { service: gdrive, path: a } => the path trying to create should end with `/`
await op.createDir('a')
```

There seems to be no way to create a directory whose name does not contain a trailing slash.

<img width="495" alt="Image" src="https://github.com/user-attachments/assets/34ffbcc7-3667-4b27-b715-4a14c23f199f" />

### Expected Behavior

While calling `await op.createDir('a/')`, a directory named 'a' rather than 'a/' should be created

### Additional Context

_No response_

### Are you willing to submit a PR to fix this bug?

- [ ] Yes, I would like to submit a PR.


Discussion / hints:
I'm guessing we need to trim the `/` after name:

https://github.com/apache/opendal/blob/410f7edf633a6abe1feccec76f34724ec7a95606/core/src/services/gdrive/core.rs#L396-L405
It seems `create_dir` isn't the only method not working as expected with gdrive, a majority of the methods related to gdrive seem to have some issues with the slashes as well. Examples:

| code(Node.js) | result | note |
|---|---|---|
|`await op.write('/a/b/c.txt', 'hello!')`|<img width="354" alt="Image" src="https://github.com/user-attachments/assets/4a027cfc-a43e-4852-a6e3-47fef07f1196" />| the names of parents should be 'a' and 'b' rather than 'a/' and 'b/' |
|`await op.list('/a/')`<br><br><img width="340" alt="Image" src="https://github.com/user-attachments/assets/d07dc95b-0d78-47c3-bbe6-ef5800db3037" />|`[]`|should be ['b2', 'b1', 'b']|
|`await op.read('/a/b/c.txt')`<br><br><img width="333" alt="Image" src="https://github.com/user-attachments/assets/6922f134-259e-4c53-b7cf-2415c1e31d14" />|[Error: NotFound (permanent) at read, context: { service: gdrive, path: a/b/c.txt, range: 0- } => path not found: a/b/c.txt]|should return the content of the file|

Fix patch:
diff --git a/core/src/raw/path_cache.rs b/core/src/raw/path_cache.rs
index 249fcb297b4..72bf1c5233e 100644
--- a/core/src/raw/path_cache.rs
+++ b/core/src/raw/path_cache.rs
@@ -47,7 +47,7 @@ pub trait PathQuery {
 ///
 /// OpenDAL is designed for path based storage systems, such as S3, HDFS, etc. But there are many
 /// services that are not path based, such as OneDrive, Google Drive, etc. For these services, we
-/// lookup files based on id. The lookup of id is very expensive, so we cache the path to id mapping
+/// look up files based on id. The lookup of id is very expensive, so we cache the path to id mapping
 /// in PathCacher.
 ///
 /// # Behavior
diff --git a/core/src/services/gdrive/core.rs b/core/src/services/gdrive/core.rs
index eaace611852..b9353aafb85 100644
--- a/core/src/services/gdrive/core.rs
+++ b/core/src/services/gdrive/core.rs
@@ -354,7 +354,10 @@ impl PathQuery for GdrivePathQuery {
             // Make sure name has been replaced with escaped name.
             //
             // ref: <https://developers.google.com/drive/api/guides/ref-search-terms>
-            format!("name = '{}'", name.replace('\'', "\\'")),
+            format!(
+                "name = '{}'",
+                name.replace('\'', "\\'").trim_end_matches('/')
+            ),
             format!("'{}' in parents", parent_id),
             "trashed = false".to_string(),
         ];
@@ -397,7 +400,7 @@ impl PathQuery for GdrivePathQuery {
         let url = "https://www.googleapis.com/drive/v3/files";
 
         let content = serde_json::to_vec(&json!({
-            "name": name,
+            "name": name.trim_end_matches('/'),
             "mimeType": "application/vnd.google-apps.folder",
             // If the parent is not provided, the folder will be created in the root folder.
             "parents": [parent_id],


Test patch: