automatons_github/resource/
file.rs

1use std::fmt::{Display, Formatter};
2
3use serde::{Deserialize, Serialize};
4use url::Url;
5
6use crate::resource::GitSha;
7
8/// File in a repository
9///
10/// Git repositories store files and directories. They can be queried using GitHub's [contents] API.
11/// The API returns a file object with a set of metadata, e.g. the file size, name, and path. The
12/// file's content is embedded in the response up to a certain size, and encoded using the file's
13/// encoding.
14#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
15pub struct File {
16    name: String,
17    path: String,
18    #[serde(with = "serde_bytes")]
19    content: Vec<u8>,
20    sha: GitSha,
21    url: Url,
22    git_url: Url,
23    html_url: Url,
24    download_url: Url,
25}
26
27impl File {
28    /// Initializes a new file
29    #[allow(clippy::too_many_arguments)]
30    pub(crate) fn new(
31        name: String,
32        path: String,
33        content: Vec<u8>,
34        sha: GitSha,
35        url: Url,
36        git_url: Url,
37        html_url: Url,
38        download_url: Url,
39    ) -> Self {
40        Self {
41            name,
42            path,
43            content,
44            sha,
45            url,
46            git_url,
47            html_url,
48            download_url,
49        }
50    }
51
52    /// Returns the file name.
53    #[cfg_attr(feature = "tracing", tracing::instrument)]
54    pub fn name(&self) -> &String {
55        &self.name
56    }
57
58    /// Returns the path of the file.
59    #[cfg_attr(feature = "tracing", tracing::instrument)]
60    pub fn path(&self) -> &String {
61        &self.path
62    }
63
64    /// Returns the file contents.
65    #[cfg_attr(feature = "tracing", tracing::instrument)]
66    pub fn content(&self) -> &[u8] {
67        &self.content
68    }
69
70    /// Returns the SHA of the Git commit to which the file belongs.
71    #[cfg_attr(feature = "tracing", tracing::instrument)]
72    pub fn sha(&self) -> &GitSha {
73        &self.sha
74    }
75
76    /// Returns the API endpoint to query the file.
77    #[cfg_attr(feature = "tracing", tracing::instrument)]
78    pub fn url(&self) -> &Url {
79        &self.url
80    }
81
82    /// Returns the API endpoint to query the file's Git commit.
83    #[cfg_attr(feature = "tracing", tracing::instrument)]
84    pub fn git_url(&self) -> &Url {
85        &self.git_url
86    }
87
88    /// Returns the URL to the account.
89    #[cfg_attr(feature = "tracing", tracing::instrument)]
90    pub fn html_url(&self) -> &Url {
91        &self.html_url
92    }
93
94    /// Returns a temporary URL to download the file.
95    ///
96    /// Download URLs expire and are meant to be used just once. To ensure the download URL does not
97    /// expire, please use the contents API to obtain a fresh download URL for each download.
98    #[cfg_attr(feature = "tracing", tracing::instrument)]
99    pub fn download_url(&self) -> &Url {
100        &self.download_url
101    }
102}
103
104impl Display for File {
105    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106        write!(f, "{}", self.path)
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use url::Url;
113
114    use super::File;
115
116    #[rustfmt::skip]
117    fn file() -> File {
118        File {
119            name: "README.md".into(),
120            path: "README.md".into(),
121            content: "ZW5jb2RlZCBjb250ZW50IC4uLg==".into(),
122            sha: "3d21ec53a331a6f037a91c368710b99387d012c1".into(),
123            url: Url::parse("https://api.github.com/repos/octokit/octokit.rb/contents/README.md").unwrap(),
124            git_url: Url::parse("https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1").unwrap(),
125            html_url: Url::parse("https://github.com/octokit/octokit.rb/blob/master/README.md").unwrap(),
126            download_url: Url::parse("https://raw.githubusercontent.com/octokit/octokit.rb/master/README.md").unwrap(),
127        }
128    }
129
130    #[test]
131    fn trait_deserialize() {
132        let json = r#"
133        {
134          "type": "file",
135          "encoding": "base64",
136          "size": 5362,
137          "name": "README.md",
138          "path": "README.md",
139          "content": "ZW5jb2RlZCBjb250ZW50IC4uLg==",
140          "sha": "3d21ec53a331a6f037a91c368710b99387d012c1",
141          "url": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md",
142          "git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
143          "html_url": "https://github.com/octokit/octokit.rb/blob/master/README.md",
144          "download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/README.md",
145          "_links": {
146            "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1",
147            "self": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md",
148            "html": "https://github.com/octokit/octokit.rb/blob/master/README.md"
149          }
150        }
151        "#;
152
153        let file: File = serde_json::from_str(json).unwrap();
154
155        assert_eq!("README.md", file.name());
156    }
157
158    #[test]
159    fn trait_display() {
160        assert_eq!("README.md", file().to_string());
161    }
162
163    #[test]
164    fn trait_send() {
165        fn assert_send<T: Send>() {}
166        assert_send::<File>();
167    }
168
169    #[test]
170    fn trait_sync() {
171        fn assert_sync<T: Sync>() {}
172        assert_sync::<File>();
173    }
174}