first usable version

This commit is contained in:
Sebastian Hugentobler 2017-03-20 16:20:37 +01:00
parent 5ce0b953cb
commit b714f3b2fc
7 changed files with 117 additions and 41 deletions

10
Cargo.lock generated
View File

@ -10,7 +10,6 @@ dependencies = [
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -353,14 +352,6 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-bidi"
version = "0.2.5"
@ -475,7 +466,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
"checksum time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade"
"checksum toml 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3474f3c6eaf32eedb4f4a66a26214f020f828a6d96c37e38a35e3a379bbcfd11"
"checksum unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a078ebdd62c0e71a709c3d53d2af693fe09fe93fbff8344aebe289b78f9032"
"checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff"
"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"

View File

@ -12,6 +12,5 @@ log = "0.3"
serde = "0.9.11"
serde_derive = "0.9.11"
serde_json = "0.9"
toml = "0.3.1"
url = "1.4.0"
xdg = "2.1.0"

View File

@ -1,16 +1,15 @@
use serde::{Deserialize, Serialize};
use serde_json;
use std::fs::File;
use std;
use std::io::{Read, Write};
use toml;
use xdg;
use xdg::BaseDirectories;
#[derive(Debug)]
pub enum ConfigError {
IOError(std::io::Error),
TomlDeError(toml::de::Error),
TomlSeError(toml::ser::Error),
SerdeJsonError(serde_json::Error),
}
impl From<std::io::Error> for ConfigError {
@ -19,26 +18,20 @@ impl From<std::io::Error> for ConfigError {
}
}
impl From<toml::de::Error> for ConfigError {
fn from(e: toml::de::Error) -> Self {
ConfigError::TomlDeError(e)
impl From<serde_json::Error> for ConfigError {
fn from(e: serde_json::Error) -> Self {
ConfigError::SerdeJsonError(e)
}
}
impl From<toml::ser::Error> for ConfigError {
fn from(e: toml::ser::Error) -> Self {
ConfigError::TomlSeError(e)
}
}
pub struct Config {
pub struct ConfigFiles {
pub xdg_dirs: BaseDirectories,
}
impl Config {
pub fn new() -> Config {
impl ConfigFiles {
pub fn new() -> ConfigFiles {
let xdg_dirs = xdg::BaseDirectories::with_prefix("gog-sync").unwrap();
Config { xdg_dirs: xdg_dirs }
ConfigFiles { xdg_dirs: xdg_dirs }
}
pub fn load<T>(&self, name: &str) -> Result<T, ConfigError>
@ -50,9 +43,9 @@ impl Config {
let mut config_contents = String::new();
config_file.read_to_string(&mut config_contents)?;
match toml::from_str(config_contents.as_str()) {
match serde_json::from_str(config_contents.as_str()) {
Ok(value) => Ok(value),
Err(error) => Err(ConfigError::TomlDeError(error)),
Err(error) => Err(ConfigError::SerdeJsonError(error)),
}
}
@ -60,7 +53,7 @@ impl Config {
where T: Serialize
{
let config_path = self.xdg_dirs.place_config_file(name)?;
let content_toml = toml::to_string(content)?;
let content_toml = serde_json::to_string_pretty(content)?;
let mut config_file = File::create(&config_path)?;
match config_file.write_all(content_toml.as_bytes()) {

View File

@ -1,10 +1,12 @@
use config::{Config, ConfigError};
use configfiles::{ConfigFiles, ConfigError};
use http::{Http, HttpError};
use models;
use models::{Token, Game, Installer};
use models::{Token, Game, Installer, Config};
use serde_json;
use serde_json::Value;
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::Path;
@ -62,8 +64,8 @@ impl<'a> Gog<'a> {
}
pub fn login(&mut self) -> Result<(), GogError> {
let config = Config::new();
let mut token: Token = match config.load("token.toml") {
let config = ConfigFiles::new();
let mut token: Token = match config.load("token.json") {
Ok(value) => value,
Err(_) => {
let code = self.get_code()?;
@ -75,7 +77,7 @@ impl<'a> Gog<'a> {
token = self.refresh_token(token.refresh_token.as_str())?;
}
config.save("token.toml", &token)?;
config.save("token.json", &token)?;
let auth_header = format!("Authorization: Bearer {token}", token = token.access_token);
self.http_client.add_header(auth_header.as_str())?;
@ -84,34 +86,91 @@ impl<'a> Gog<'a> {
}
pub fn sync(&mut self, storage_path: &str) -> Result<(), GogError> {
let configfiles = ConfigFiles::new();
let mut config: Config = match configfiles.load("config.json") {
Ok(value) => value,
Err(_) => {
error!("Configuration error, generating new one...");
Config {
storage: String::from(storage_path),
games: HashMap::new(),
installers: HashMap::new(),
extras: HashMap::new(),
}
}
};
let game_ids = self.get_game_ids()?;
for game_id in game_ids {
let game_hash_saved = match config.games.get(&game_id.to_string()) {
Some(value) => value.clone(),
None => u64::min_value(),
};
let game = self.get_game(game_id)?;
let game_hash = models::get_hash(&game);
if game_hash_saved == game_hash {
info!("{} already up to date.", &game.title);
continue;
}
let game_root = Path::new(storage_path).join(&game.title);
fs::create_dir_all(&game_root)?;
if !game.cd_key.is_empty() {
let key_path = Path::new(game_root.as_os_str()).join("key.txt");
let mut key_file = File::create(&key_path)?;
key_file.write_all(game.cd_key.as_bytes())?
}
for installer in game.installers {
let installer_hash_saved = match config.installers.get(&installer.manual_url) {
Some(value) => value.clone(),
None => u64::min_value(),
};
let installer_hash = models::get_hash(&installer);
if installer_hash_saved == installer_hash {
info!("{} already up to date.", &installer.manual_url);
continue;
}
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)?;
config.installers.insert(installer.manual_url, installer_hash);
configfiles.save("config.json", &config)?;
}
for extra in game.extras {
let extra_hash_saved = match config.installers.get(&extra.manual_url) {
Some(value) => value.clone(),
None => u64::min_value(),
};
let extra_hash = models::get_hash(&extra);
if extra_hash_saved == extra_hash {
info!("{} already up to date.", &extra.manual_url);
continue;
}
let extra_uri = format!("https://embed.gog.com{}", extra.manual_url);
info!("downloading {} for {}...", &extra.name, &game.title);
self.http_client.download(extra_uri.as_str(), &game_root)?;
config.extras.insert(extra.manual_url, extra_hash);
configfiles.save("config.json", &config)?;
}
config.games.insert(game_id.to_string(), game_hash);
configfiles.save("config.json", &config)?;
}
Ok(())
@ -186,6 +245,10 @@ impl<'a> Gog<'a> {
continue;
}
if [1, 2, 3, 4, 5].contains(&game_id_parsed) {
continue;
}
game_ids.push(game_id_parsed);
}

View File

@ -45,7 +45,7 @@ impl From<url::ParseError> for HttpError {
}
impl From<HttpError> for WriteError {
fn from(e: HttpError) -> Self {
fn from(_: HttpError) -> Self {
WriteError::__Nonexhaustive
}
}

View File

@ -8,19 +8,20 @@ extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate url;
extern crate xdg;
mod config;
mod configfiles;
mod gog;
mod http;
mod models;
use config::Config;
use configfiles::ConfigFiles;
use clap::{Arg, App};
use gog::Gog;
use http::Http;
use models::Config;
use std::collections::HashMap;
fn main() {
env_logger::init().unwrap();
@ -37,9 +38,23 @@ fn main() {
.takes_value(true))
.get_matches();
let download_folder = matches.value_of("storage").unwrap_or(".");
let configfiles = ConfigFiles::new();
let config: Config = match configfiles.load("config.json") {
Ok(value) => value,
Err(_) => {
Config {
storage: String::from("."),
games: HashMap::new(),
installers: HashMap::new(),
extras: HashMap::new(),
}
}
};
let config = Config::new();
let download_folder: &str = match matches.value_of("storage") {
Some(value) => value,
None => config.storage.as_str(),
};
let mut http_client = Http::new();
let mut gog = Gog::new(&mut http_client);

View File

@ -1,6 +1,7 @@
use chrono::UTC;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
#[derive(Serialize, Deserialize)]
@ -28,6 +29,21 @@ fn timestamp() -> i64 {
UTC::now().timestamp()
}
#[derive(Serialize, Deserialize)]
pub struct Config {
pub storage: String,
#[serde(default = "default_map")]
pub games: HashMap<String, u64>,
#[serde(default = "default_map")]
pub installers: HashMap<String, u64>,
#[serde(default = "default_map")]
pub extras: HashMap<String, u64>,
}
fn default_map() -> HashMap<String, u64> {
HashMap::new()
}
#[derive(Hash)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]