add documentation and license
This commit is contained in:
parent
b714f3b2fc
commit
7be923459e
8 changed files with 540 additions and 7 deletions
|
@ -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
|
||||
{
|
||||
|
|
27
src/gog.rs
27
src/gog.rs
|
@ -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)?;
|
||||
|
|
38
src/http.rs
38
src/http.rs
|
@ -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)
|
||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue