first usable version
This commit is contained in:
parent
5ce0b953cb
commit
b714f3b2fc
7 changed files with 117 additions and 41 deletions
|
@ -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()) {
|
73
src/gog.rs
73
src/gog.rs
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -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);
|
||||
|
|
|
@ -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")]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue