Merge pull request 'feat: implement ssh_url deserialization to url::Url instead of String' (#53) from Aviac/forgejo-api:feat/parse-ssh-url into main
				
					
				
			Reviewed-on: https://codeberg.org/Cyborus/forgejo-api/pulls/53 Reviewed-by: Cyborus <cyborus@noreply.codeberg.org>
This commit is contained in:
		
						commit
						80cba9a6c3
					
				
					 5 changed files with 148 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -63,7 +63,7 @@ pub fn create_struct_for_definition(
 | 
			
		|||
                crate::schema_subtype_name(spec, name, prop_name, value, &mut field_ty)?;
 | 
			
		||||
                crate::schema_subtypes(spec, name, prop_name, value, &mut subtypes)?;
 | 
			
		||||
            }
 | 
			
		||||
            if field_name.ends_with("url") && field_name != "ssh_url" && field_ty == "String" {
 | 
			
		||||
            if field_name.ends_with("url") && field_ty == "String" {
 | 
			
		||||
                field_ty = "url::Url".into()
 | 
			
		||||
            }
 | 
			
		||||
            if field_ty == name {
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +73,16 @@ pub fn create_struct_for_definition(
 | 
			
		|||
                field_ty = format!("Option<{field_ty}>")
 | 
			
		||||
            }
 | 
			
		||||
            if field_ty == "Option<url::Url>" {
 | 
			
		||||
                fields.push_str("#[serde(deserialize_with = \"crate::none_if_blank_url\")]\n");
 | 
			
		||||
                if field_name == "ssh_url" {
 | 
			
		||||
                    fields.push_str(
 | 
			
		||||
                        "#[serde(deserialize_with = \"crate::deserialize_optional_ssh_url\")]\n",
 | 
			
		||||
                    );
 | 
			
		||||
                } else {
 | 
			
		||||
                    fields.push_str("#[serde(deserialize_with = \"crate::none_if_blank_url\")]\n");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if field_ty == "url::Url" && field_name == "ssh_url" {
 | 
			
		||||
                fields.push_str("#[serde(deserialize_with = \"crate::deserialize_ssh_url\")]\n");
 | 
			
		||||
            }
 | 
			
		||||
            if field_ty == "time::OffsetDateTime" {
 | 
			
		||||
                fields.push_str("#[serde(with = \"time::serde::rfc3339\")]\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -106,8 +115,8 @@ pub fn create_struct_for_definition(
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Some(additonal_schema) = &schema.additional_properties {
 | 
			
		||||
        let prop_ty = crate::schema_ref_type_name(spec, additonal_schema)?;
 | 
			
		||||
    if let Some(additional_schema) = &schema.additional_properties {
 | 
			
		||||
        let prop_ty = crate::schema_ref_type_name(spec, additional_schema)?;
 | 
			
		||||
        fields.push_str("#[serde(flatten)]\n");
 | 
			
		||||
        fields.push_str("pub additional: BTreeMap<String, ");
 | 
			
		||||
        fields.push_str(&prop_ty);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2278,7 +2278,8 @@ pub struct Repository {
 | 
			
		|||
    pub release_counter: Option<u64>,
 | 
			
		||||
    pub repo_transfer: Option<RepoTransfer>,
 | 
			
		||||
    pub size: Option<u64>,
 | 
			
		||||
    pub ssh_url: Option<String>,
 | 
			
		||||
    #[serde(deserialize_with = "crate::deserialize_optional_ssh_url")]
 | 
			
		||||
    pub ssh_url: Option<url::Url>,
 | 
			
		||||
    pub stars_count: Option<u64>,
 | 
			
		||||
    pub template: Option<bool>,
 | 
			
		||||
    #[serde(with = "time::serde::rfc3339::option")]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										36
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										36
									
								
								src/lib.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
use reqwest::{Client, Request, StatusCode};
 | 
			
		||||
use serde::{Deserialize, Deserializer};
 | 
			
		||||
use soft_assert::*;
 | 
			
		||||
use url::Url;
 | 
			
		||||
use zeroize::Zeroize;
 | 
			
		||||
| 
						 | 
				
			
			@ -254,6 +255,41 @@ fn none_if_blank_url<'de, D: serde::Deserializer<'de>>(
 | 
			
		|||
    deserializer.deserialize_str(EmptyUrlVisitor)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)] // not used yet, but it might appear in the future
 | 
			
		||||
fn deserialize_ssh_url<'de, D, DE>(deserializer: D) -> Result<Url, DE>
 | 
			
		||||
where
 | 
			
		||||
    D: Deserializer<'de>,
 | 
			
		||||
    DE: serde::de::Error,
 | 
			
		||||
{
 | 
			
		||||
    let raw_url: String = String::deserialize(deserializer).map_err(DE::custom)?;
 | 
			
		||||
    parse_ssh_url(&raw_url).map_err(DE::custom)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn deserialize_optional_ssh_url<'de, D, DE>(deserializer: D) -> Result<Option<Url>, DE>
 | 
			
		||||
where
 | 
			
		||||
    D: Deserializer<'de>,
 | 
			
		||||
    DE: serde::de::Error,
 | 
			
		||||
{
 | 
			
		||||
    let raw_url: Option<String> = Option::deserialize(deserializer).map_err(DE::custom)?;
 | 
			
		||||
    raw_url
 | 
			
		||||
        .as_ref()
 | 
			
		||||
        .map(parse_ssh_url)
 | 
			
		||||
        .map(|res| res.map_err(DE::custom))
 | 
			
		||||
        .transpose()
 | 
			
		||||
        .or(Ok(None))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_ssh_url(raw_url: &String) -> Result<Url, url::ParseError> {
 | 
			
		||||
    // in case of a non-standard ssh-port (not 22), the ssh url coming from the forgejo API
 | 
			
		||||
    // is actually parseable by the url crate, so try to do that first
 | 
			
		||||
    Url::parse(raw_url).or_else(|_| {
 | 
			
		||||
        // otherwise the ssh url is not parseable by the url crate and we try again after some
 | 
			
		||||
        // pre-processing
 | 
			
		||||
        let url = format!("ssh://{url}", url = raw_url.replace(":", "/"));
 | 
			
		||||
        Url::parse(url.as_str())
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<structs::DefaultMergeStyle> for structs::MergePullRequestOptionDo {
 | 
			
		||||
    fn from(value: structs::DefaultMergeStyle) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -430,3 +430,9 @@ async fn admin() {
 | 
			
		|||
        .await
 | 
			
		||||
        .expect("failed to delete hook");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn ssh_url_deserialization() {
 | 
			
		||||
    let data = include_str!("./repo_data.json");
 | 
			
		||||
    assert!(serde_json::from_str::<Repository>(data).is_ok());
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										91
									
								
								tests/repo_data.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								tests/repo_data.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
{
 | 
			
		||||
  "id": 160106,
 | 
			
		||||
  "owner": {
 | 
			
		||||
    "id": 94809,
 | 
			
		||||
    "login": "Cyborus",
 | 
			
		||||
    "login_name": "",
 | 
			
		||||
    "full_name": "",
 | 
			
		||||
    "email": "cyborus@noreply.codeberg.org",
 | 
			
		||||
    "avatar_url": "https://codeberg.org/avatars/53e78f627539c6a0b96854028529779133724a5df2d2c229e5d0eb48aaa3d1fa",
 | 
			
		||||
    "language": "",
 | 
			
		||||
    "is_admin": false,
 | 
			
		||||
    "last_login": "0001-01-01T00:00:00Z",
 | 
			
		||||
    "created": "2023-04-30T00:54:15Z",
 | 
			
		||||
    "restricted": false,
 | 
			
		||||
    "active": false,
 | 
			
		||||
    "prohibit_login": false,
 | 
			
		||||
    "location": "",
 | 
			
		||||
    "pronouns": "",
 | 
			
		||||
    "website": "https://cyborus.xyz",
 | 
			
		||||
    "description": "I host my own Forgejo instance at https://code.cartoon-aa.xyz/",
 | 
			
		||||
    "visibility": "public",
 | 
			
		||||
    "followers_count": 4,
 | 
			
		||||
    "following_count": 4,
 | 
			
		||||
    "starred_repos_count": 8,
 | 
			
		||||
    "username": "Cyborus"
 | 
			
		||||
  },
 | 
			
		||||
  "name": "forgejo-api",
 | 
			
		||||
  "full_name": "Cyborus/forgejo-api",
 | 
			
		||||
  "description": "Rust crate to interact with the Forgejo API",
 | 
			
		||||
  "empty": false,
 | 
			
		||||
  "private": false,
 | 
			
		||||
  "fork": false,
 | 
			
		||||
  "template": false,
 | 
			
		||||
  "parent": null,
 | 
			
		||||
  "mirror": false,
 | 
			
		||||
  "size": 1481,
 | 
			
		||||
  "language": "Rust",
 | 
			
		||||
  "languages_url": "https://codeberg.org/api/v1/repos/Cyborus/forgejo-api/languages",
 | 
			
		||||
  "html_url": "https://codeberg.org/Cyborus/forgejo-api",
 | 
			
		||||
  "url": "https://codeberg.org/api/v1/repos/Cyborus/forgejo-api",
 | 
			
		||||
  "link": "",
 | 
			
		||||
  "ssh_url": "git@codeberg.org:Cyborus/forgejo-api.git",
 | 
			
		||||
  "clone_url": "https://codeberg.org/Cyborus/forgejo-api.git",
 | 
			
		||||
  "original_url": "",
 | 
			
		||||
  "website": "",
 | 
			
		||||
  "stars_count": 4,
 | 
			
		||||
  "forks_count": 1,
 | 
			
		||||
  "watchers_count": 2,
 | 
			
		||||
  "open_issues_count": 2,
 | 
			
		||||
  "open_pr_counter": 0,
 | 
			
		||||
  "release_counter": 2,
 | 
			
		||||
  "default_branch": "main",
 | 
			
		||||
  "archived": false,
 | 
			
		||||
  "created_at": "2023-11-09T17:42:18Z",
 | 
			
		||||
  "updated_at": "2024-04-27T22:42:52Z",
 | 
			
		||||
  "archived_at": "1970-01-01T00:00:00Z",
 | 
			
		||||
  "permissions": {
 | 
			
		||||
    "admin": false,
 | 
			
		||||
    "push": false,
 | 
			
		||||
    "pull": true
 | 
			
		||||
  },
 | 
			
		||||
  "has_issues": true,
 | 
			
		||||
  "internal_tracker": {
 | 
			
		||||
    "enable_time_tracker": true,
 | 
			
		||||
    "allow_only_contributors_to_track_time": true,
 | 
			
		||||
    "enable_issue_dependencies": true
 | 
			
		||||
  },
 | 
			
		||||
  "has_wiki": false,
 | 
			
		||||
  "wiki_branch": "master",
 | 
			
		||||
  "has_pull_requests": true,
 | 
			
		||||
  "has_projects": true,
 | 
			
		||||
  "has_releases": true,
 | 
			
		||||
  "has_packages": false,
 | 
			
		||||
  "has_actions": false,
 | 
			
		||||
  "ignore_whitespace_conflicts": false,
 | 
			
		||||
  "allow_merge_commits": true,
 | 
			
		||||
  "allow_rebase": true,
 | 
			
		||||
  "allow_rebase_explicit": true,
 | 
			
		||||
  "allow_squash_merge": true,
 | 
			
		||||
  "allow_fast_forward_only_merge": false,
 | 
			
		||||
  "allow_rebase_update": true,
 | 
			
		||||
  "default_delete_branch_after_merge": true,
 | 
			
		||||
  "default_merge_style": "merge",
 | 
			
		||||
  "default_allow_maintainer_edit": true,
 | 
			
		||||
  "avatar_url": "",
 | 
			
		||||
  "internal": false,
 | 
			
		||||
  "mirror_interval": "",
 | 
			
		||||
  "object_format_name": "sha1",
 | 
			
		||||
  "mirror_updated": "0001-01-01T00:00:00Z",
 | 
			
		||||
  "repo_transfer": null
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue