Skip to content

Commit 1e54ad0

Browse files
committed
Fetching gist metadata from GitHub
1 parent 1ab3684 commit 1e54ad0

File tree

4 files changed

+92
-33
lines changed

4 files changed

+92
-33
lines changed

src/gist/info.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,26 +66,38 @@ impl Info {
6666

6767
/// Builder for the gist Info struct.
6868
#[derive(Clone, Debug)]
69-
pub struct Builder {
69+
pub struct InfoBuilder {
7070
data: BTreeMap<Datum, Value>,
7171
}
7272

73-
impl Builder {
73+
impl InfoBuilder {
7474
#[inline]
7575
pub fn new() -> Self {
76-
Builder{data: BTreeMap::new()}
76+
InfoBuilder{data: BTreeMap::new()}
7777
}
7878

7979
#[inline]
80-
pub fn set<V: ?Sized>(mut self, datum: Datum, value: &V) -> Self
80+
pub fn with<V: ?Sized>(mut self, datum: Datum, value: &V) -> Self
81+
where Value: Borrow<V>, V: ToOwned<Owned=Value>
82+
{
83+
self.set(datum, value); self
84+
}
85+
86+
#[inline]
87+
pub fn without(mut self, datum: Datum) -> Self {
88+
self.unset(datum); self
89+
}
90+
91+
#[inline]
92+
pub fn set<V: ?Sized>(&mut self, datum: Datum, value: &V) -> &mut Self
8193
where Value: Borrow<V>, V: ToOwned<Owned=Value>
8294
{
8395
self.data.insert(datum, value.to_owned());
8496
self
8597
}
8698

8799
#[inline]
88-
pub fn unset(mut self, datum: Datum) -> Self {
100+
pub fn unset(&mut self, datum: Datum,) -> &mut Self {
89101
self.data.remove(&datum); self
90102
}
91103

@@ -98,7 +110,7 @@ impl Builder {
98110

99111
#[cfg(test)]
100112
mod tests {
101-
use super::{Datum, Builder};
113+
use super::{Datum, InfoBuilder};
102114

103115
#[test]
104116
fn datum_order_id_always_first() {
@@ -124,7 +136,7 @@ mod tests {
124136

125137
#[test]
126138
fn info_empty() {
127-
let info = Builder::new().build();
139+
let info = InfoBuilder::new().build();
128140
for datum in Datum::iter_variants() {
129141
assert!(!info.has(datum));
130142
assert_eq!(datum.default_value(), *info.get(datum));
@@ -134,7 +146,7 @@ mod tests {
134146
#[test]
135147
fn info_regular() {
136148
let id = String::from("some_id");
137-
let info = Builder::new()
149+
let info = InfoBuilder::new()
138150
.set(Datum::Id, &id)
139151
.set(Datum::Owner, "JohnDoe")
140152
.set(Datum::Description, "Amazing gist")

src/gist/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ mod uri;
1010
use std::path::PathBuf;
1111

1212
use super::{BIN_DIR, GISTS_DIR};
13+
pub use self::info::{Datum, Info, InfoBuilder};
1314
pub use self::uri::{Uri, UriError};
1415

1516

16-
1717
/// Structure representing a single gist.
1818
#[derive(Debug, Clone, Eq, Hash)]
1919
pub struct Gist {

src/hosts/github.rs

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use url::Url;
1818

1919
use super::super::USER_AGENT;
2020
use ext::hyper::header::Link;
21-
use gist::{self, Gist};
21+
use gist::{self, Datum, Gist};
2222
use util::{mark_executable, symlink_file};
2323
use super::Host;
2424

@@ -42,17 +42,15 @@ impl Host for GitHub {
4242
/// If the gist hasn't been downloaded already, a clone of the gist's Git repo is performed.
4343
/// Otherwise, it's just a simple Git pull.
4444
fn fetch_gist(&self, gist: &Gist) -> io::Result<()> {
45-
if gist.uri.host_id != "gh" {
46-
return Err(io::Error::new(io::ErrorKind::InvalidData, format!(
47-
"expected a GitHub Gist, but got a '{}' one", gist.uri.host_id)));
48-
}
49-
45+
try!(ensure_github_gist(gist));
5046
let gist = try!(resolve_gist(gist));
47+
5148
if gist.is_local() {
5249
try!(update_gist(gist));
5350
} else {
5451
try!(clone_gist(gist));
5552
}
53+
5654
Ok(())
5755
}
5856

@@ -65,6 +63,29 @@ impl Host for GitHub {
6563
url.set_path(&format!("{}/{}", gist.uri.owner, gist.id.as_ref().unwrap()));
6664
Ok(url.into_string())
6765
}
66+
67+
/// Return a structure with gist metadata.
68+
fn gist_info(&self, gist: &Gist) -> io::Result<Option<gist::Info>> {
69+
try!(ensure_github_gist(gist));
70+
let gist = try!(resolve_gist(gist));
71+
72+
let info = try!(get_gist_info(gist.id.as_ref().unwrap()));
73+
74+
// Build the gist::Info structure from known keys in the gist info JSON.
75+
const INFO_FIELDS: &'static [(Datum, &'static str)] = &[
76+
(Datum::Id, "id"),
77+
(Datum::Description, "description"),
78+
(Datum::Url, "html_url"),
79+
(Datum::CreatedAt, "created_at"),
80+
(Datum::UpdatedAt, "updated_at"),
81+
];
82+
let mut result = gist::InfoBuilder::new();
83+
for &(datum, field) in INFO_FIELDS {
84+
result.set(datum, info[field].as_string().unwrap());
85+
}
86+
result.set(Datum::Owner, info["owner"]["login"].as_string().unwrap());
87+
Ok(Some(result.build()))
88+
}
6889
}
6990

7091
/// Base URL to gist HTML pages.
@@ -158,24 +179,10 @@ fn clone_gist<G: AsRef<Gist>>(gist: G) -> io::Result<()> {
158179
// Talk to GitHub to obtain the URL that we can clone the gist from
159180
// as a Git repository.
160181
let clone_url = {
161-
let http = Client::new();
162-
163-
let gist_url = {
164-
let mut url = Url::parse(API_URL).unwrap();
165-
url.set_path(&format!("gists/{}", gist.id.as_ref().unwrap()));
166-
url.into_string()
167-
};
168-
169-
debug!("Getting GitHub gist info from {}", gist_url);
170-
let mut resp = try!(http.get(&gist_url)
171-
.header(UserAgent(USER_AGENT.clone()))
172-
.send()
173-
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)));
174-
let gist_info_json = read_json(&mut resp);
175-
176-
let clone_url = gist_info_json["git_pull_url"].as_string().unwrap().to_owned();
182+
let gist_info = try!(get_gist_info(&gist.id.as_ref().unwrap()));
183+
let clone_url = gist_info["git_pull_url"].as_string().unwrap().to_owned();
177184
trace!("GitHub gist #{} has a git_pull_url=\"{}\"",
178-
gist_info_json["id"].as_string().unwrap(), clone_url);
185+
gist_info["id"].as_string().unwrap(), clone_url);
179186
clone_url
180187
};
181188

@@ -263,8 +270,37 @@ fn list_gists(owner: &str) -> Vec<Gist> {
263270
}
264271

265272

273+
/// Retrieve information/metadata about a gist.
274+
/// Returns a Json object with the parsed GitHub response.
275+
fn get_gist_info(gist_id: &str) -> io::Result<Json> {
276+
let http = Client::new();
277+
278+
let gist_url = {
279+
let mut url = Url::parse(API_URL).unwrap();
280+
url.set_path(&format!("gists/{}", gist_id));
281+
url.into_string()
282+
};
283+
284+
debug!("Getting GitHub gist info from {}", gist_url);
285+
let mut resp = try!(http.get(&gist_url)
286+
.header(UserAgent(USER_AGENT.clone()))
287+
.send()
288+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)));
289+
Ok(read_json(&mut resp))
290+
}
291+
292+
266293
// Utility functions
267294

295+
/// Check if given Gist is a GitHub gist. Invoke using try!().
296+
fn ensure_github_gist(gist: &Gist) -> io::Result<()> {
297+
if gist.uri.host_id != "gh" {
298+
return Err(io::Error::new(io::ErrorKind::InvalidData, format!(
299+
"expected a GitHub Gist, but got a '{}' one", gist.uri.host_id)));
300+
}
301+
Ok(())
302+
}
303+
268304
/// Read HTTP response from hype and parse it as JSON.
269305
fn read_json(response: &mut Response) -> Json {
270306
let mut body = match response.headers.get::<ContentLength>() {

src/hosts/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::collections::HashMap;
1111
use std::io;
1212
use std::sync::Arc;
1313

14-
use super::gist::Gist;
14+
use super::gist::{self, Gist};
1515

1616

1717
/// Represents a gists' host: a (web) service that hosts gists (code snippets).
@@ -30,6 +30,17 @@ pub trait Host : Send + Sync {
3030
/// Return a URL to a HTML page that can display the gist.
3131
/// This may involving talking to the remote host.
3232
fn gist_url(&self, gist: &Gist) -> io::Result<String>;
33+
34+
/// Return a structure with information/metadata about the gist.
35+
///
36+
/// Note: The return type for this method is io::Result<Option<Info>>
37+
/// rather than Option<io::Result<Info>> because the availability of
38+
/// gist metadata may be gist-specific (i.e. some gists have it,
39+
/// some don't).
40+
fn gist_info(&self, _: &Gist) -> io::Result<Option<gist::Info>> {
41+
// This default indicates the host doesn't expose any gist metadata.
42+
Ok(None)
43+
}
3344
}
3445

3546

0 commit comments

Comments
 (0)