Even better leniant parsing
This commit is contained in:
		
							parent
							
								
									fbcc5d536e
								
							
						
					
					
						commit
						f1a0944688
					
				| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
{"nItems":0,"nPages":0,"items":[],"_links":[{"href":"/api/v1/project/vogon","rel":"project","type":"GET"},{"href":"/api/v1/project/vogon/posts?page=998","rel":"prev","type":"GET"}]}
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
use serde::{Deserialize, Deserializer};
 | 
			
		||||
 | 
			
		||||
pub fn cohost_posts_api_url(project: impl AsRef<str>, page: u64) -> String {
 | 
			
		||||
    format!(
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ pub fn cohost_posts_api_url(project: impl AsRef<str>, page: u64) -> String {
 | 
			
		|||
 | 
			
		||||
// Cohost doesn't give us Next links ("rel: next") for further pages, so we'll have to ALWAYS populate the rel=next field
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
pub struct CohostPostsPage {
 | 
			
		||||
    #[serde(rename = "nItems")]
 | 
			
		||||
    pub number_items: usize,
 | 
			
		||||
| 
						 | 
				
			
			@ -22,19 +22,27 @@ pub struct CohostPostsPage {
 | 
			
		|||
    pub links: Vec<CohostPostLink>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
pub struct CohostPost {
 | 
			
		||||
    #[serde(rename = "postId")]
 | 
			
		||||
    pub id: u64,
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    #[serde(deserialize_with = "deserialize_null_default", default)]
 | 
			
		||||
    pub headline: String,
 | 
			
		||||
    #[serde(rename = "publishedAt")]
 | 
			
		||||
    pub published_at: DateTime<Utc>,
 | 
			
		||||
    pub cws: Vec<String>,
 | 
			
		||||
    pub tags: Vec<String>,
 | 
			
		||||
    #[serde(rename = "plainTextBody", default)]
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename = "plainTextBody",
 | 
			
		||||
        deserialize_with = "deserialize_null_default",
 | 
			
		||||
        default
 | 
			
		||||
    )]
 | 
			
		||||
    pub plain_body: String,
 | 
			
		||||
    #[serde(rename = "singlePostPageUrl")]
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename = "singlePostPageUrl",
 | 
			
		||||
        deserialize_with = "deserialize_null_default",
 | 
			
		||||
        default
 | 
			
		||||
    )]
 | 
			
		||||
    pub url: String,
 | 
			
		||||
    #[serde(rename = "postingProject")]
 | 
			
		||||
    pub poster: CohostPostingProject,
 | 
			
		||||
| 
						 | 
				
			
			@ -42,31 +50,49 @@ pub struct CohostPost {
 | 
			
		|||
    pub share_tree: Vec<CohostPost>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
pub struct CohostPostingProject {
 | 
			
		||||
    #[serde(rename = "projectId")]
 | 
			
		||||
    pub id: u64,
 | 
			
		||||
    #[serde(deserialize_with = "deserialize_null_default", default)]
 | 
			
		||||
    pub handle: String,
 | 
			
		||||
    #[serde(rename = "displayName", default)]
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename = "displayName",
 | 
			
		||||
        deserialize_with = "deserialize_null_default",
 | 
			
		||||
        default
 | 
			
		||||
    )]
 | 
			
		||||
    pub display_name: String,
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    #[serde(deserialize_with = "deserialize_null_default", default)]
 | 
			
		||||
    pub dek: String,
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    #[serde(deserialize_with = "deserialize_null_default", default)]
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    #[serde(deserialize_with = "deserialize_null_default", default)]
 | 
			
		||||
    pub pronouns: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
pub struct CohostPostLink {
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    #[serde(deserialize_with = "deserialize_null_default", default)]
 | 
			
		||||
    pub href: String,
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    #[serde(deserialize_with = "deserialize_null_default", default)]
 | 
			
		||||
    pub rel: String,
 | 
			
		||||
    #[serde(rename = "type", default)]
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename = "type",
 | 
			
		||||
        deserialize_with = "deserialize_null_default",
 | 
			
		||||
        default
 | 
			
		||||
    )]
 | 
			
		||||
    pub t_type: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
 | 
			
		||||
where
 | 
			
		||||
    T: Default + Deserialize<'de>,
 | 
			
		||||
    D: Deserializer<'de>,
 | 
			
		||||
{
 | 
			
		||||
    let opt = Option::deserialize(deserializer)?;
 | 
			
		||||
    Ok(opt.unwrap_or_default())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_deserialize() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let post_page_json = include_str!("../samples/cohost/api/v1/project_posts.json");
 | 
			
		||||
| 
						 | 
				
			
			@ -77,3 +103,19 @@ fn test_deserialize() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		|||
    assert_eq!(post.poster.id, 32693);
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_deserialize_weird() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let post_page_json = include_str!("../samples/cohost/api/v1/vogon_pathological.json");
 | 
			
		||||
    let _post_page_actual: CohostPostsPage = serde_json::from_str(post_page_json)?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_deserialize_empty() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    let post_page_json = include_str!("../samples/cohost/api/v1/empty_posts_age.json");
 | 
			
		||||
    let post_page_actual: CohostPostsPage = serde_json::from_str(post_page_json)?;
 | 
			
		||||
    println!("{:?}", post_page_actual);
 | 
			
		||||
    assert!(post_page_actual.items.is_empty());
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue