Fix repository paths containing spaces (#62)

This commit is contained in:
Georg Bauer
2026-08-15 11:41:04 +02:00
parent efdf5ee4c3
commit 8e2feac18e
3 changed files with 62 additions and 21 deletions

View File

@@ -374,9 +374,10 @@ answer before uploading it to App Store Connect.
- [ ] Switch a repository between **History** and **Files**. Each mode displays - [ ] Switch a repository between **History** and **Files**. Each mode displays
the expected content and switching back preserves normal navigation. the expected content and switching back preserves normal navigation.
- [ ] In Files, traverse several nested folders using rows, the navigation-bar - [ ] In Files, traverse several nested folders using rows, including a folder
Back button, and the left-edge swipe. Folder contents and titles match the and file whose names contain spaces (for example below Chezmoi's
repository hierarchy. `private_Library`), the navigation-bar Back button, and the left-edge
swipe. Folder contents, files, and titles match the repository hierarchy.
- [ ] In the repository root and in several nested folders, switch between - [ ] In the repository root and in several nested folders, switch between
**Files** and **History**. Each folder history contains only commits that **Files** and **History**. Each folder history contains only commits that
affect that folder or its descendants. Switch back to Files and verify the affect that folder or its descendants. Switch back to Files and verify the

View File

@@ -12,6 +12,10 @@ use crate::{
}; };
use gitea_openapi::apis; use gitea_openapi::apis;
fn encode_path(path: &str) -> String {
apis::urlencode(path).replace('+', "%20")
}
impl Client { impl Client {
pub async fn current_user_repositories_page( pub async fn current_user_repositories_page(
&self, &self,
@@ -99,7 +103,7 @@ impl Client {
"repos/{}/{}/contents/{}", "repos/{}/{}/contents/{}",
apis::urlencode(&repository.owner), apis::urlencode(&repository.owner),
apis::urlencode(&repository.repository), apis::urlencode(&repository.repository),
apis::urlencode(path), encode_path(path),
); );
self.execute(self.request(Method::GET, &endpoint)?) self.execute(self.request(Method::GET, &endpoint)?)
.await? .await?
@@ -110,15 +114,14 @@ impl Client {
} }
pub async fn repository_file(&self, repository: &RepositoryId, path: &str) -> Result<Vec<u8>> { pub async fn repository_file(&self, repository: &RepositoryId, path: &str) -> Result<Vec<u8>> {
apis::repository_api::repo_get_raw_file( let endpoint = format!(
&self.configuration(), "repos/{}/{}/raw/{}",
&repository.owner, apis::urlencode(&repository.owner),
&repository.repository, apis::urlencode(&repository.repository),
path, encode_path(path),
None, );
) self.execute(self.request(Method::GET, &endpoint)?)
.await .await?
.map_err(Error::generated)?
.bytes() .bytes()
.await .await
.map(|bytes| bytes.to_vec()) .map(|bytes| bytes.to_vec())

View File

@@ -110,7 +110,7 @@ async fn reads_nested_directory_from_compatible_contents_endpoint() {
let mut buffer = [0; 4096]; let mut buffer = [0; 4096];
let length = stream.read(&mut buffer).unwrap(); let length = stream.read(&mut buffer).unwrap();
let request = String::from_utf8_lossy(&buffer[..length]).into_owned(); let request = String::from_utf8_lossy(&buffer[..length]).into_owned();
let body = r#"[{"name":"guide.md","path":"docs/guide.md","type":"file"}]"#; let body = r#"[{"name":"guide.md","path":"docs/private guide/guide.md","type":"file"}]"#;
write!( write!(
stream, stream,
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
@@ -125,16 +125,53 @@ async fn reads_nested_directory_from_compatible_contents_endpoint() {
.unwrap(); .unwrap();
let repository = RepositoryId::new("forgejo", "forgejo").unwrap(); let repository = RepositoryId::new("forgejo", "forgejo").unwrap();
let contents = client let contents = client
.repository_contents(&repository, "docs/guide") .repository_contents(&repository, "docs/private guide")
.await .await
.unwrap(); .unwrap();
assert_eq!(contents.len(), 1); assert_eq!(contents.len(), 1);
assert_eq!(contents[0].path.as_deref(), Some("docs/guide.md")); assert_eq!(
contents[0].path.as_deref(),
Some("docs/private guide/guide.md")
);
assert!( assert!(
server server
.join() .join()
.unwrap() .unwrap()
.starts_with("GET /api/v1/repos/forgejo/forgejo/contents/docs%2Fguide ") .starts_with("GET /api/v1/repos/forgejo/forgejo/contents/docs%2Fprivate%20guide ")
);
}
#[tokio::test]
async fn reads_file_with_spaces_from_raw_endpoint() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let address = listener.local_addr().unwrap();
let server = thread::spawn(move || {
let (mut stream, _) = listener.accept().unwrap();
let mut buffer = [0; 4096];
let length = stream.read(&mut buffer).unwrap();
let request = String::from_utf8_lossy(&buffer[..length]).into_owned();
let body = "contents";
write!(
stream,
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
body.len()
)
.unwrap();
request
});
let client = Client::new(&format!("http://{address}"), None).unwrap();
let repository = RepositoryId::new("gitea", "gitea").unwrap();
let contents = client
.repository_file(&repository, "docs/private guide/read me.md")
.await
.unwrap();
assert_eq!(contents, b"contents");
assert!(
server.join().unwrap().starts_with(
"GET /api/v1/repos/gitea/gitea/raw/docs%2Fprivate%20guide%2Fread%20me.md "
)
); );
} }