add documentation and license

This commit is contained in:
Sebastian Hugentobler 2017-03-20 19:00:27 +01:00
parent b714f3b2fc
commit 7be923459e
8 changed files with 540 additions and 7 deletions

View file

@ -1,3 +1,10 @@
//! Loads and saves configuration as json files.
//!
//! Follows the [xdg](https://crates.io/crates/xdg) requirements and uses a
//! prefix of `gog-sync`.
//!
//! Uses [serde_json](https://crates.io/crates/serde_json) for serialization/deserialisation.
use serde::{Deserialize, Serialize};
use serde_json;
use std::fs::File;
@ -6,6 +13,7 @@ use std::io::{Read, Write};
use xdg;
use xdg::BaseDirectories;
/// Wraps `std::io::Error` and `serde_json::Error`.
#[derive(Debug)]
pub enum ConfigError {
IOError(std::io::Error),
@ -25,15 +33,38 @@ impl From<serde_json::Error> for ConfigError {
}
pub struct ConfigFiles {
pub xdg_dirs: BaseDirectories,
xdg_dirs: BaseDirectories,
}
impl ConfigFiles {
/// Create a new instance of `ConfigFiles`.
/// This sets the xdg prefix to `gog-sync`.
///
/// # Example
/// ```
/// use configfiles::ConfigFiles;
///
/// let configfiles = ConfigFiles::new();
/// ```
pub fn new() -> ConfigFiles {
let xdg_dirs = xdg::BaseDirectories::with_prefix("gog-sync").unwrap();
ConfigFiles { xdg_dirs: xdg_dirs }
}
/// Load configuration from a json file.
///
/// # Example
/// ```
/// use configfiles::ConfigFiles;
///
/// #[derive(Serialize, Deserialize)]
/// pub struct Config {
/// pub manual_url: String,
/// }
///
/// let configfiles = ConfigFiles::new();
/// let config: Config = configfiles.load("config.json");
/// ```
pub fn load<T>(&self, name: &str) -> Result<T, ConfigError>
where T: Deserialize
{
@ -49,6 +80,20 @@ impl ConfigFiles {
}
}
/// Save configuration to a json file.
///
/// # Example
/// ```
/// use configfiles::ConfigFiles;
///
/// #[derive(Serialize, Deserialize)]
/// pub struct Config {
/// pub manual_url: String,
/// }
///
/// let configfiles = ConfigFiles::new();
/// let config: Config = configfiles.load("config.json");
/// ```
pub fn save<T>(&self, name: &str, content: &T) -> Result<(), ConfigError>
where T: Serialize
{

View file

@ -1,3 +1,15 @@
//! Uses the GOG api as described [here](https://gogapidocs.readthedocs.io/en/latest/).
//!
//! # Example
//! ```
//! use gog::Gog;
//!
//! let mut http_client = Http::new();
//! let mut gog = Gog::new(&mut http_client);
//! gog.login();
//! gog.sync(download_folder);
//! ```
use configfiles::{ConfigFiles, ConfigError};
use http::{Http, HttpError};
use models;
@ -11,6 +23,7 @@ use std::io;
use std::io::Write;
use std::path::Path;
/// Wraps `ConfigError`, `HttpError`, `serde_json::Error`, and `io::Error`.
#[derive(Debug)]
pub enum GogError {
Error(&'static str),
@ -44,6 +57,8 @@ impl From<io::Error> for GogError {
}
}
/// Wraps the GOG api.
/// At the moment Client ID and Client Secret are hardcoded to the galaxy client.
pub struct Gog<'a> {
client_id: String,
client_secret: String,
@ -53,6 +68,7 @@ pub struct Gog<'a> {
}
impl<'a> Gog<'a> {
/// Create a new instance of `Gog`.
pub fn new(http_client: &'a mut Http) -> Gog<'a> {
Gog {
client_id: String::from("46899977096215655"),
@ -63,6 +79,10 @@ impl<'a> Gog<'a> {
}
}
/// Logs into a gog account.
/// If a token was already saved it should work automatically. Otherwise you
/// will get an url which you have to open in a browser. There you have to
/// login and copy the code parameter from the next page into the prompt.
pub fn login(&mut self) -> Result<(), GogError> {
let config = ConfigFiles::new();
let mut token: Token = match config.load("token.json") {
@ -85,6 +105,8 @@ impl<'a> Gog<'a> {
Ok(())
}
/// Syncs the contents of a gog account with a local folder.
/// Uses a hash to figure out whether something has changed.
pub fn sync(&mut self, storage_path: &str) -> Result<(), GogError> {
let configfiles = ConfigFiles::new();
let mut config: Config = match configfiles.load("config.json") {
@ -137,12 +159,15 @@ impl<'a> Gog<'a> {
continue;
}
let installer_root = Path::new(&game_root).join(&installer.language);
fs::create_dir_all(&installer_root)?;
let installer_uri = format!("https://embed.gog.com{}", installer.manual_url);
info!("downloading {} for {}...",
&installer.manual_url,
&game.title);
self.http_client.download(installer_uri.as_str(), &game_root)?;
self.http_client.download(installer_uri.as_str(), &installer_root)?;
config.installers.insert(installer.manual_url, installer_hash);
configfiles.save("config.json", &config)?;

View file

@ -1,3 +1,6 @@
//! A thin wrapper around [curl-rust](https://crates.io/crates/curl) to make
//! get requests and download files.
use curl;
use curl::easy::{Easy, List, WriteError};
use std::fs;
@ -11,6 +14,7 @@ use std::str::Utf8Error;
use url;
use url::Url;
/// Wraps `curl::Error`, `Utf8Error`, `io::Error` and `url::ParseError`.
#[derive(Debug)]
pub enum HttpError {
Error(&'static str),
@ -50,11 +54,14 @@ impl From<HttpError> for WriteError {
}
}
/// Wraps curl-rust.
pub struct Http {
curl: Easy,
}
impl Http {
/// Create a new instance of `Http`.
/// Ensures that curl follows redirects.
pub fn new() -> Http {
let mut curl = Easy::new();
curl.follow_location(true).unwrap();
@ -62,6 +69,15 @@ impl Http {
Http { curl: curl }
}
/// Make a get request and return the response.
///
/// # Example
/// ```
/// use http::Http;
///
/// let mut http_client = Http::new();
/// http_client.get("https://discworld.com/");
/// ```
pub fn get(&mut self, uri: &str) -> Result<String, HttpError> {
let mut response_body = String::new();
@ -79,6 +95,15 @@ impl Http {
Ok(response_body)
}
/// Add a header to all future requests.
///
/// # Example
/// ```
/// use http::Http;
///
/// let mut http_client = Http::new();
/// http_client.add_header("X-Clacks-Overhead: GNU Terry Pratchett");
/// ```
pub fn add_header(&mut self, header: &str) -> Result<(), HttpError> {
let mut list = List::new();
list.append(header)?;
@ -89,6 +114,19 @@ impl Http {
}
}
/// Download a file to the specified folder without creating the folder.
///
/// The filename is taken from the last URI segment.
/// # Example
/// ```
/// use http::Http;
/// use std::path::Path;
///
/// let mut http_client = Http::new();
///
/// let download_path = Path::new("/opt/bin");
/// http_client.download("https://example.com/sed", &download_path);
/// ```
pub fn download(&mut self,
download_uri: &str,
download_dir: &PathBuf)

View file

@ -1,3 +1,17 @@
//! Synchronizes a [GOG](https://www.gog.com/) library with a local folder.
//!
//! ```
//! USAGE:
//! gog-sync [OPTIONS]
//!
//! FLAGS:
//! -h, --help Prints help information
//! -V, --version Prints version information
//!
//! OPTIONS:
//! -s, --storage <FOLDER> Sets the download folder (defaults to the working directory).
//! ```
extern crate chrono;
extern crate clap;
extern crate curl;
@ -11,9 +25,9 @@ extern crate serde_derive;
extern crate url;
extern crate xdg;
mod configfiles;
mod gog;
mod http;
pub mod configfiles;
pub mod gog;
pub mod http;
mod models;
use configfiles::ConfigFiles;