support returning header values
This commit is contained in:
		
							parent
							
								
									99ae09c8d3
								
							
						
					
					
						commit
						996c722f90
					
				
					 2 changed files with 126 additions and 52 deletions
				
			
		| 
						 | 
				
			
			@ -153,52 +153,87 @@ fn query_struct_name(op: &Operation) -> eyre::Result<String> {
 | 
			
		|||
    Ok(ty)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<String> {
 | 
			
		||||
    let mut names = op
 | 
			
		||||
fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<ResponseType> {
 | 
			
		||||
    let mut responses = op
 | 
			
		||||
        .responses
 | 
			
		||||
        .http_codes
 | 
			
		||||
        .iter()
 | 
			
		||||
        .filter(|(k, _)| k.starts_with("2"))
 | 
			
		||||
        .map(|(_, v)| response_ref_type_name(spec, v))
 | 
			
		||||
        .collect::<Result<Vec<_>, _>>()?;
 | 
			
		||||
    let mut iter = responses.into_iter();
 | 
			
		||||
    let mut response = iter
 | 
			
		||||
        .next()
 | 
			
		||||
        .ok_or_else(|| eyre::eyre!("must have at least one response type"))?;
 | 
			
		||||
    for next in iter {
 | 
			
		||||
        response = response.merge(next)?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    names.sort();
 | 
			
		||||
    names.dedup();
 | 
			
		||||
    let name = match names.len() {
 | 
			
		||||
        0 => eyre::bail!("no type name found"),
 | 
			
		||||
        1 => {
 | 
			
		||||
            let name = names.pop().unwrap();
 | 
			
		||||
            if name == "empty" {
 | 
			
		||||
                "()".into()
 | 
			
		||||
            } else {
 | 
			
		||||
                name
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        2 if names[0] == "empty" || names[1] == "empty" => {
 | 
			
		||||
            let name = if names[0] == "empty" {
 | 
			
		||||
                names.remove(1)
 | 
			
		||||
            } else {
 | 
			
		||||
                names.remove(0)
 | 
			
		||||
            };
 | 
			
		||||
            format!("Option<{name}>")
 | 
			
		||||
        }
 | 
			
		||||
        _ => eyre::bail!("too many possible return types"),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(name)
 | 
			
		||||
    Ok(response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn response_ref_type_name(spec: &OpenApiV2, schema: &MaybeRef<Response>) -> eyre::Result<String> {
 | 
			
		||||
    let (name, response) = deref_response(spec, schema)?;
 | 
			
		||||
    if let Some(schema) = &response.schema {
 | 
			
		||||
        schema_ref_type_name(spec, schema)
 | 
			
		||||
    } else if let Some(name) = name {
 | 
			
		||||
        Ok(name.into())
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok("()".into())
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
struct ResponseType {
 | 
			
		||||
    headers: Option<String>,
 | 
			
		||||
    body: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ResponseType {
 | 
			
		||||
    fn merge(self, other: Self) -> eyre::Result<Self> {
 | 
			
		||||
        let mut new = Self::default();
 | 
			
		||||
        match (self.headers, other.headers) {
 | 
			
		||||
            (Some(a), Some(b)) if a != b => eyre::bail!("incompatible header types in response"),
 | 
			
		||||
            (Some(a), None) => new.headers = Some(format!("Option<{a}>")),
 | 
			
		||||
            (None, Some(b)) => new.headers = Some(format!("Option<{b}>")),
 | 
			
		||||
            (a, b) => new.headers = a.or(b),
 | 
			
		||||
        };
 | 
			
		||||
        match (self.body.as_deref(), other.body.as_deref()) {
 | 
			
		||||
            (Some(a), Some(b)) if a != b => eyre::bail!("incompatible header types in response"),
 | 
			
		||||
            (Some(a), Some("()") | None) => new.body = Some(format!("Option<{a}>")),
 | 
			
		||||
            (Some("()") | None, Some(b)) => new.body = Some(format!("Option<{b}>")),
 | 
			
		||||
            (a, b) => new.body = self.body.or(other.body),
 | 
			
		||||
        };
 | 
			
		||||
        Ok(new)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for ResponseType {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        let mut tys = Vec::new();
 | 
			
		||||
        tys.extend(self.headers.as_deref());
 | 
			
		||||
        tys.extend(self.body.as_deref());
 | 
			
		||||
        match tys[..] {
 | 
			
		||||
            [single] => f.write_str(single),
 | 
			
		||||
            _ => {
 | 
			
		||||
                write!(f, "(")?;
 | 
			
		||||
                for (i, item) in tys.iter().copied().enumerate() {
 | 
			
		||||
                    f.write_str(item)?;
 | 
			
		||||
                    if i + 1 < tys.len() {
 | 
			
		||||
                        write!(f, ", ")?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                write!(f, ")")?;
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn response_ref_type_name(
 | 
			
		||||
    spec: &OpenApiV2,
 | 
			
		||||
    schema: &MaybeRef<Response>,
 | 
			
		||||
) -> eyre::Result<ResponseType> {
 | 
			
		||||
    let (_, response) = deref_response(spec, schema)?;
 | 
			
		||||
    let mut ty = ResponseType::default();
 | 
			
		||||
    if response.headers.is_some() {
 | 
			
		||||
        ty.headers = Some("reqwest::header::HeaderMap".into());
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(schema) = &response.schema {
 | 
			
		||||
        ty.body = Some(schema_ref_type_name(spec, schema)?);
 | 
			
		||||
    };
 | 
			
		||||
    Ok(ty)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn schema_ref_type_name(spec: &OpenApiV2, schema: &MaybeRef<Schema>) -> eyre::Result<String> {
 | 
			
		||||
    let (name, schema) = deref_definition(spec, &schema)?;
 | 
			
		||||
    schema_type_name(spec, name, schema)
 | 
			
		||||
| 
						 | 
				
			
			@ -503,16 +538,17 @@ fn create_method_response(
 | 
			
		|||
    let mut has_empty = false;
 | 
			
		||||
    let mut only_empty = true;
 | 
			
		||||
    for (code, res) in &op.responses.http_codes {
 | 
			
		||||
        let name = response_ref_type_name(spec, res)?;
 | 
			
		||||
        let response = response_ref_type_name(spec, res)?;
 | 
			
		||||
        if !code.starts_with("2") {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if name == "()" || name == "empty" {
 | 
			
		||||
        if matches!(response.body.as_deref(), Some("()") | None) {
 | 
			
		||||
            has_empty = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            only_empty = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let fn_ret = fn_return_from_op(spec, op)?;
 | 
			
		||||
    let optional = has_empty && !only_empty;
 | 
			
		||||
    let mut out = String::new();
 | 
			
		||||
    out.push_str("let response = self.execute(request).await?;\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -523,32 +559,70 @@ fn create_method_response(
 | 
			
		|||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        out.push_str(code);
 | 
			
		||||
        out.push_str(" => ");
 | 
			
		||||
        let handler = match &res.schema {
 | 
			
		||||
        out.push_str(" => Ok(");
 | 
			
		||||
        let mut handlers = Vec::new();
 | 
			
		||||
        let header_handler = match &res.headers {
 | 
			
		||||
            Some(_) => {
 | 
			
		||||
                if fn_ret
 | 
			
		||||
                    .headers
 | 
			
		||||
                    .as_ref()
 | 
			
		||||
                    .map(|s| s.starts_with("Option<"))
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                {
 | 
			
		||||
                    Some("Some(response.headers().clone())")
 | 
			
		||||
                } else {
 | 
			
		||||
                    Some("response.headers().clone()")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                if fn_ret.headers.is_some() {
 | 
			
		||||
                    dbg!(&fn_ret);
 | 
			
		||||
                    panic!();
 | 
			
		||||
                    Some("None")
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        handlers.extend(header_handler);
 | 
			
		||||
        let body_handler = match &res.schema {
 | 
			
		||||
            Some(schema) if schema_is_string(spec, schema)? => {
 | 
			
		||||
                if optional {
 | 
			
		||||
                    "Ok(Some(response.text().await?))"
 | 
			
		||||
                    Some("Some(response.text().await?)")
 | 
			
		||||
                } else {
 | 
			
		||||
                    "Ok(response.text().await?)"
 | 
			
		||||
                    Some("response.text().await?")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Some(_) => {
 | 
			
		||||
                if optional {
 | 
			
		||||
                    "Ok(Some(response.json().await?))"
 | 
			
		||||
                    Some("Some(response.json().await?)")
 | 
			
		||||
                } else {
 | 
			
		||||
                    "Ok(response.json().await?)"
 | 
			
		||||
                    Some("response.json().await?")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                if optional {
 | 
			
		||||
                    "Ok(None)"
 | 
			
		||||
                    Some("None")
 | 
			
		||||
                } else {
 | 
			
		||||
                    "Ok(())"
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        out.push_str(handler);
 | 
			
		||||
        out.push_str(",\n");
 | 
			
		||||
        handlers.extend(body_handler);
 | 
			
		||||
        match handlers[..] {
 | 
			
		||||
            [single] => out.push_str(single),
 | 
			
		||||
            _ => {
 | 
			
		||||
                out.push('(');
 | 
			
		||||
                for (i, item) in handlers.iter().copied().enumerate() {
 | 
			
		||||
                    out.push_str(item);
 | 
			
		||||
                    if i + 1 < handlers.len() {
 | 
			
		||||
                        out.push_str(", ");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                out.push(')');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        out.push_str("),\n");
 | 
			
		||||
    }
 | 
			
		||||
    out.push_str("_ => Err(ForgejoError::UnexpectedStatusCode(response.status()))\n");
 | 
			
		||||
    out.push_str("}\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1864,7 +1864,7 @@ impl crate::Forgejo {
 | 
			
		|||
        owner: &str,
 | 
			
		||||
        repo: &str,
 | 
			
		||||
        query: RepoGetAllCommitsQuery,
 | 
			
		||||
    ) -> Result<Vec<Commit>, ForgejoError> {
 | 
			
		||||
    ) -> Result<(reqwest::header::HeaderMap, Vec<Commit>), ForgejoError> {
 | 
			
		||||
        let request = self
 | 
			
		||||
            .get(&format!(
 | 
			
		||||
                "repos/{owner}/{repo}/commits?{}",
 | 
			
		||||
| 
						 | 
				
			
			@ -1873,7 +1873,7 @@ impl crate::Forgejo {
 | 
			
		|||
            .build()?;
 | 
			
		||||
        let response = self.execute(request).await?;
 | 
			
		||||
        match response.status().as_u16() {
 | 
			
		||||
            200 => Ok(response.json().await?),
 | 
			
		||||
            200 => Ok((response.headers().clone(), response.json().await?)),
 | 
			
		||||
            _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -4572,7 +4572,7 @@ impl crate::Forgejo {
 | 
			
		|||
        repo: &str,
 | 
			
		||||
        index: u64,
 | 
			
		||||
        query: RepoGetPullRequestCommitsQuery,
 | 
			
		||||
    ) -> Result<Vec<Commit>, ForgejoError> {
 | 
			
		||||
    ) -> Result<(reqwest::header::HeaderMap, Vec<Commit>), ForgejoError> {
 | 
			
		||||
        let request = self
 | 
			
		||||
            .get(&format!(
 | 
			
		||||
                "repos/{owner}/{repo}/pulls/{index}/commits?{}",
 | 
			
		||||
| 
						 | 
				
			
			@ -4581,7 +4581,7 @@ impl crate::Forgejo {
 | 
			
		|||
            .build()?;
 | 
			
		||||
        let response = self.execute(request).await?;
 | 
			
		||||
        match response.status().as_u16() {
 | 
			
		||||
            200 => Ok(response.json().await?),
 | 
			
		||||
            200 => Ok((response.headers().clone(), response.json().await?)),
 | 
			
		||||
            _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -4597,7 +4597,7 @@ impl crate::Forgejo {
 | 
			
		|||
        repo: &str,
 | 
			
		||||
        index: u64,
 | 
			
		||||
        query: RepoGetPullRequestFilesQuery,
 | 
			
		||||
    ) -> Result<Vec<ChangedFile>, ForgejoError> {
 | 
			
		||||
    ) -> Result<(reqwest::header::HeaderMap, Vec<ChangedFile>), ForgejoError> {
 | 
			
		||||
        let request = self
 | 
			
		||||
            .get(&format!(
 | 
			
		||||
                "repos/{owner}/{repo}/pulls/{index}/files?{}",
 | 
			
		||||
| 
						 | 
				
			
			@ -4606,7 +4606,7 @@ impl crate::Forgejo {
 | 
			
		|||
            .build()?;
 | 
			
		||||
        let response = self.execute(request).await?;
 | 
			
		||||
        match response.status().as_u16() {
 | 
			
		||||
            200 => Ok(response.json().await?),
 | 
			
		||||
            200 => Ok((response.headers().clone(), response.json().await?)),
 | 
			
		||||
            _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue