se_dump/
post.rs

1use crate::date::UtcDate;
2use crate::user::UserId;
3use serde::Deserialize;
4use serde_repr::Deserialize_repr;
5use std::fmt::{Debug, Formatter};
6
7/// Dump of all posts.
8///
9/// You can use this to parse a `Posts.xml` file using, for example, `quick_xml`:
10///
11/// ```
12/// use std::fs::File;
13/// use std::io::BufReader;
14/// use std::path::Path;
15/// use quick_xml::de::from_reader;
16/// use se_dump::post::{PostId, Posts};
17///
18/// let reader = BufReader::new(File::open(Path::new("sample_data/Posts.xml")).unwrap());
19/// let posts: Posts = from_reader(reader).unwrap();
20/// assert_eq!(posts.posts[0].id, PostId(2115))
21/// ```
22#[derive(Deserialize, Debug)]
23pub struct Posts {
24    #[serde(rename = "$value", default)]
25    pub posts: Vec<Post>,
26}
27
28#[derive(Deserialize, PartialOrd, PartialEq, Ord, Eq, Copy, Clone, Hash)]
29pub struct PostId(pub i32);
30
31impl Debug for PostId {
32    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
33        self.0.fmt(f)
34    }
35}
36
37// Would be kinda cool if this was an enum, with corresponding Question, Answer, etc types. Then we could avoid some Options. But https://github.com/serde-rs/serde/issues/745
38#[derive(Deserialize, Debug, PartialOrd, PartialEq, Ord, Eq, Clone, Hash)]
39pub struct Post {
40    #[serde(rename = "@Id")]
41    pub id: PostId,
42
43    #[serde(rename = "@PostTypeId")]
44    pub post_type: PostType,
45
46    /// AcceptedAnswerId (only present on questions)
47    #[serde(rename = "@AcceptedAnswerId")]
48    pub accepted_answer_id: Option<PostId>,
49
50    /// ParentId (only present on answers)
51    #[serde(rename = "@ParentId")]
52    pub parent_id: Option<PostId>,
53
54    #[serde(rename = "@CreationDate")]
55    pub creation_date: UtcDate,
56
57    #[serde(rename = "@Score")]
58    pub score: i32,
59
60    #[serde(rename = "@ViewCount")]
61    pub view_count: Option<i32>,
62
63    /// Body (as rendered HTML, not Markdown)
64    #[serde(rename = "@Body")]
65    pub body: String,
66
67    /// OwnerUserId (only present if user has not been deleted; always -1 for tag wiki entries, i.e. the community user owns them)
68    #[serde(rename = "@OwnerUserId")]
69    pub owner_user_id: Option<UserId>,
70
71    #[serde(rename = "@OwnerDisplayName")]
72    pub owner_display_name: Option<String>,
73
74    #[serde(rename = "@LastEditorUserId")]
75    pub last_editor_user_id: Option<UserId>,
76
77    #[serde(rename = "@LastEditorDisplayName")]
78    pub last_editor_display_name: Option<String>,
79
80    #[serde(rename = "@LastEditDate")]
81    pub last_edit_date: Option<UtcDate>,
82
83    #[serde(rename = "@LastActivityDate")]
84    pub last_activity_date: UtcDate,
85
86    #[serde(rename = "@Title")]
87    pub title: Option<String>,
88
89    #[serde(rename = "@Tags")]
90    pub tags: Option<String>,
91
92    #[serde(rename = "@AnswerCount")]
93    pub answer_count: Option<i32>,
94
95    #[serde(rename = "@CommentCount")]
96    pub comment_count: Option<i32>,
97
98    #[serde(rename = "@FavoriteCount")]
99    pub favorite_count: Option<i32>,
100
101    #[serde(rename = "@ClosedDate")]
102    pub closed_date: Option<UtcDate>,
103
104    #[serde(rename = "@CommunityOwnedDate")]
105    pub community_owned_date: Option<UtcDate>,
106
107    #[serde(rename = "@ContentLicense")]
108    pub content_license: String,
109}
110
111#[derive(Deserialize_repr, Debug, PartialOrd, PartialEq, Ord, Eq, Copy, Clone, Hash)]
112#[repr(i32)]
113pub enum PostType {
114    Question = 1,
115    Answer = 2,
116    OrphanedTagWiki = 3,
117    TagWikiExcerpt = 4,
118    TagWiki = 5,
119    ModeratorNomination = 6,
120    WikiPlaceholder = 7,
121    PrivilegeWiki = 8,
122}