mirror of https://github.com/vitalif/grive2
refactored the creation of resources
parent
7830136518
commit
6db0b4a7aa
|
@ -48,8 +48,10 @@ int main( int argc, char **argv )
|
||||||
DefaultLog nofile_log ;
|
DefaultLog nofile_log ;
|
||||||
LogBase::Inst( &nofile_log ) ;
|
LogBase::Inst( &nofile_log ) ;
|
||||||
|
|
||||||
|
Json options ;
|
||||||
|
|
||||||
int c ;
|
int c ;
|
||||||
while ((c = getopt(argc, argv, "al:vV")) != -1)
|
while ((c = getopt(argc, argv, "al:vVf")) != -1)
|
||||||
{
|
{
|
||||||
switch ( c )
|
switch ( c )
|
||||||
{
|
{
|
||||||
|
@ -72,7 +74,6 @@ int main( int argc, char **argv )
|
||||||
|
|
||||||
// save to config
|
// save to config
|
||||||
config.Get().Add( "refresh_token", Json( token.RefreshToken() ) ) ;
|
config.Get().Add( "refresh_token", Json( token.RefreshToken() ) ) ;
|
||||||
assert( config.Get()["refresh_token"].Str() == token.RefreshToken() ) ;
|
|
||||||
config.Save() ;
|
config.Save() ;
|
||||||
|
|
||||||
break ;
|
break ;
|
||||||
|
@ -97,6 +98,12 @@ int main( int argc, char **argv )
|
||||||
LogBase::Inst()->Enable( log::verbose ) ;
|
LogBase::Inst()->Enable( log::verbose ) ;
|
||||||
break ;
|
break ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'f' :
|
||||||
|
{
|
||||||
|
options.Add( "force", Json(true) ) ;
|
||||||
|
break ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +127,7 @@ int main( int argc, char **argv )
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
OAuth2 token( refresh_token, client_id, client_secret ) ;
|
OAuth2 token( refresh_token, client_id, client_secret ) ;
|
||||||
Drive drive( token ) ;
|
Drive drive( token, options ) ;
|
||||||
|
|
||||||
drive.Update() ;
|
drive.Update() ;
|
||||||
drive.SaveState() ;
|
drive.SaveState() ;
|
||||||
|
|
|
@ -52,9 +52,9 @@ namespace
|
||||||
const std::string state_file = ".grive_state" ;
|
const std::string state_file = ".grive_state" ;
|
||||||
}
|
}
|
||||||
|
|
||||||
Drive::Drive( OAuth2& auth ) :
|
Drive::Drive( OAuth2& auth, const Json& options ) :
|
||||||
m_auth( auth ),
|
m_auth( auth ),
|
||||||
m_state( state_file )
|
m_state( state_file, options )
|
||||||
{
|
{
|
||||||
m_http_hdr.push_back( "Authorization: Bearer " + m_auth.AccessToken() ) ;
|
m_http_hdr.push_back( "Authorization: Bearer " + m_auth.AccessToken() ) ;
|
||||||
m_http_hdr.push_back( "GData-Version: 3.0" ) ;
|
m_http_hdr.push_back( "GData-Version: 3.0" ) ;
|
||||||
|
|
|
@ -34,11 +34,12 @@ namespace http
|
||||||
}
|
}
|
||||||
|
|
||||||
class OAuth2 ;
|
class OAuth2 ;
|
||||||
|
class Json ;
|
||||||
|
|
||||||
class Drive
|
class Drive
|
||||||
{
|
{
|
||||||
public :
|
public :
|
||||||
Drive( OAuth2& auth ) ;
|
Drive( OAuth2& auth, const Json& options ) ;
|
||||||
|
|
||||||
void Update() ;
|
void Update() ;
|
||||||
void Sync() ;
|
void Sync() ;
|
||||||
|
|
|
@ -48,37 +48,20 @@ Entry::Entry( const xml::Node& n )
|
||||||
}
|
}
|
||||||
|
|
||||||
/// construct an entry from a file or folder in local directory
|
/// construct an entry from a file or folder in local directory
|
||||||
Entry::Entry( const fs::path& path, const std::string& kind ) :
|
Entry::Entry( const std::string& name, const std::string& kind ) :
|
||||||
m_title ( path.filename().string() ),
|
m_title ( name ),
|
||||||
m_filename ( path.filename().string() ),
|
m_filename ( name ),
|
||||||
m_kind
|
m_kind ( kind )
|
||||||
(
|
|
||||||
fs::exists(path)
|
|
||||||
? (fs::is_directory(path) ? "folder" : "file" )
|
|
||||||
: kind
|
|
||||||
),
|
|
||||||
m_md5 ( fs::is_directory(path) ? "" : crypt::MD5( path ) ),
|
|
||||||
m_mtime ( fs::exists(path) ? os::FileMTime( path ) : DateTime() )
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry::Entry(
|
void Entry::FromLocal( const fs::path& path )
|
||||||
const std::string& name,
|
|
||||||
const std::string& resource_id,
|
|
||||||
const std::string& href,
|
|
||||||
const std::string& md5,
|
|
||||||
const std::string& kind,
|
|
||||||
const DateTime& mtime,
|
|
||||||
const std::string& parent_href ) :
|
|
||||||
m_title ( name ),
|
|
||||||
m_filename ( name ),
|
|
||||||
m_kind ( kind ),
|
|
||||||
m_md5 ( md5 ),
|
|
||||||
m_resource_id ( resource_id ),
|
|
||||||
m_parent_hrefs ( 1, parent_href ),
|
|
||||||
m_self_href ( href ),
|
|
||||||
m_mtime ( mtime )
|
|
||||||
{
|
{
|
||||||
|
m_title = path.filename().string() ;
|
||||||
|
m_filename = path.filename().string() ;
|
||||||
|
m_kind = fs::is_directory(path) ? "folder" : "file" ;
|
||||||
|
m_md5 = fs::is_directory(path) ? "" : crypt::MD5( path ) ;
|
||||||
|
m_mtime = fs::exists(path) ? os::FileMTime( path ) : DateTime() ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Entry::Update( const xml::Node& n )
|
void Entry::Update( const xml::Node& n )
|
||||||
|
|
|
@ -43,18 +43,11 @@ class Entry
|
||||||
{
|
{
|
||||||
public :
|
public :
|
||||||
Entry( ) ;
|
Entry( ) ;
|
||||||
explicit Entry( const fs::path& path, const std::string& kind = "" ) ;
|
Entry( const std::string& name, const std::string& kind ) ;
|
||||||
explicit Entry( const xml::Node& n ) ;
|
explicit Entry( const xml::Node& n ) ;
|
||||||
explicit Entry(
|
|
||||||
const std::string& name,
|
|
||||||
const std::string& resource_id,
|
|
||||||
const std::string& href,
|
|
||||||
const std::string& md5,
|
|
||||||
const std::string& kind,
|
|
||||||
const DateTime& mtime,
|
|
||||||
const std::string& parent_href ) ;
|
|
||||||
|
|
||||||
void AssignID( const Entry& entry ) ;
|
void AssignID( const Entry& entry ) ;
|
||||||
|
void FromLocal( const fs::path& path ) ;
|
||||||
|
|
||||||
std::string Title() const ;
|
std::string Title() const ;
|
||||||
std::string Filename() const ;
|
std::string Filename() const ;
|
||||||
|
|
|
@ -57,24 +57,10 @@ Resource::Resource() :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource::Resource( const xml::Node& entry ) :
|
Resource::Resource( const std::string& name, const std::string& kind ) :
|
||||||
m_entry ( entry ),
|
m_entry ( name, kind ),
|
||||||
m_parent( 0 ),
|
m_parent( 0 ),
|
||||||
m_state ( remote_new )
|
m_state ( unknown )
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Resource::Resource( const Entry& entry, Resource *parent ) :
|
|
||||||
m_entry ( entry ),
|
|
||||||
m_parent( parent ),
|
|
||||||
m_state ( remote_new )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Resource::Resource( const fs::path& path, const std::string& kind ) :
|
|
||||||
m_entry ( path, kind ),
|
|
||||||
m_parent( 0 ),
|
|
||||||
m_state ( local_new )
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +158,7 @@ void Resource::FromRemote( const Entry& remote, const DateTime& last_sync )
|
||||||
}
|
}
|
||||||
|
|
||||||
m_entry.AssignID( remote ) ;
|
m_entry.AssignID( remote ) ;
|
||||||
|
assert( m_state != unknown ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the resource with the attributes of local file or directory. This
|
/// Update the resource with the attributes of local file or directory. This
|
||||||
|
@ -181,11 +168,8 @@ void Resource::FromLocal( const DateTime& last_sync )
|
||||||
fs::path path = Path() ;
|
fs::path path = Path() ;
|
||||||
assert( fs::exists( path ) ) ;
|
assert( fs::exists( path ) ) ;
|
||||||
|
|
||||||
// root folder is always rsync
|
// root folder is always in sync
|
||||||
if ( m_parent == 0 )
|
if ( !IsRoot() )
|
||||||
m_state = sync ;
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// if the file is not created after last sync, assume file is
|
// if the file is not created after last sync, assume file is
|
||||||
// remote_deleted first, it will be updated to sync/remote_changed
|
// remote_deleted first, it will be updated to sync/remote_changed
|
||||||
|
@ -193,8 +177,12 @@ void Resource::FromLocal( const DateTime& last_sync )
|
||||||
DateTime mtime = os::FileMTime( path ) ;
|
DateTime mtime = os::FileMTime( path ) ;
|
||||||
m_state = ( mtime > last_sync ? local_new : remote_deleted ) ;
|
m_state = ( mtime > last_sync ? local_new : remote_deleted ) ;
|
||||||
|
|
||||||
|
m_entry.FromLocal( path ) ;
|
||||||
|
|
||||||
Trace( "%1% found on disk: %2%", Name(), m_state ) ;
|
Trace( "%1% found on disk: %2%", Name(), m_state ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert( m_state != unknown ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Resource::SelfHref() const
|
std::string Resource::SelfHref() const
|
||||||
|
@ -280,10 +268,12 @@ Resource* Resource::FindChild( const std::string& name )
|
||||||
// try to change the state to "sync"
|
// try to change the state to "sync"
|
||||||
void Resource::Sync( http::Agent *http, const http::Headers& auth )
|
void Resource::Sync( http::Agent *http, const http::Headers& auth )
|
||||||
{
|
{
|
||||||
|
assert( m_state != unknown ) ;
|
||||||
|
|
||||||
// root folder is already synced
|
// root folder is already synced
|
||||||
if ( IsRoot() )
|
if ( IsRoot() )
|
||||||
{
|
{
|
||||||
m_state = sync ;
|
assert( m_state == sync ) ;
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,13 +47,10 @@ public :
|
||||||
|
|
||||||
public :
|
public :
|
||||||
Resource() ;
|
Resource() ;
|
||||||
explicit Resource( const xml::Node& entry ) ;
|
Resource( const std::string& name, const std::string& kind ) ;
|
||||||
explicit Resource( const Entry& entry, Resource *parent = 0 ) ;
|
|
||||||
explicit Resource( const fs::path& path, const std::string& kind = "" ) ;
|
|
||||||
// explicit Resource( const Json& json, Resource *parent = 0 ) ;
|
|
||||||
void Swap( Resource& coll ) ;
|
|
||||||
|
|
||||||
// default copy ctor & op= are fine
|
// default copy ctor & op= are fine
|
||||||
|
void Swap( Resource& coll ) ;
|
||||||
|
|
||||||
bool IsFolder() const ;
|
bool IsFolder() const ;
|
||||||
|
|
||||||
|
@ -112,7 +109,11 @@ private :
|
||||||
remote_changed,
|
remote_changed,
|
||||||
|
|
||||||
/// Resource delete in remote, need to delete in local
|
/// Resource delete in remote, need to delete in local
|
||||||
remote_deleted
|
remote_deleted,
|
||||||
|
|
||||||
|
|
||||||
|
/// invalid value
|
||||||
|
unknown
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
friend std::ostream& operator<<( std::ostream& os, State s ) ;
|
friend std::ostream& operator<<( std::ostream& os, State s ) ;
|
||||||
|
|
|
@ -38,9 +38,14 @@
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
|
|
||||||
State::State( const fs::path& filename )
|
State::State( const fs::path& filename, const Json& options )
|
||||||
{
|
{
|
||||||
Read( filename ) ;
|
Read( filename ) ;
|
||||||
|
|
||||||
|
// the "-f" option will make grive always thinks remote is newer
|
||||||
|
Json force ;
|
||||||
|
if ( options.Get("force", force) && force.Bool() )
|
||||||
|
m_last_sync = DateTime() ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronize local directory. Build up the resource tree from files and folders
|
/// Synchronize local directory. Build up the resource tree from files and folders
|
||||||
|
@ -72,7 +77,7 @@ void State::FromLocal( const fs::path& p, gr::Resource* folder )
|
||||||
Resource *c = folder->FindChild( fname ) ;
|
Resource *c = folder->FindChild( fname ) ;
|
||||||
if ( c == 0 )
|
if ( c == 0 )
|
||||||
{
|
{
|
||||||
c = new Resource( i->path() ) ;
|
c = new Resource( fname, fs::is_directory(i->path()) ? "folder" : "file" ) ;
|
||||||
folder->AddChild( c ) ;
|
folder->AddChild( c ) ;
|
||||||
m_res.Insert( c ) ;
|
m_res.Insert( c ) ;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +154,7 @@ bool State::Update( const Entry& e )
|
||||||
else if ( e.Kind() == "folder" || !e.Filename().empty() )
|
else if ( e.Kind() == "folder" || !e.Filename().empty() )
|
||||||
{
|
{
|
||||||
// first create a dummy resource and update it later
|
// first create a dummy resource and update it later
|
||||||
child = new Resource( parent->Path() / e.Filename(), e.Kind() ) ;
|
child = new Resource( name, e.Kind() ) ;
|
||||||
parent->AddChild( child ) ;
|
parent->AddChild( child ) ;
|
||||||
m_res.Insert( child ) ;
|
m_res.Insert( child ) ;
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ public :
|
||||||
typedef ResourceTree::iterator iterator ;
|
typedef ResourceTree::iterator iterator ;
|
||||||
|
|
||||||
public :
|
public :
|
||||||
explicit State( const fs::path& filename ) ;
|
explicit State( const fs::path& filename, const Json& options ) ;
|
||||||
|
|
||||||
void FromLocal( const fs::path& p ) ;
|
void FromLocal( const fs::path& p ) ;
|
||||||
void FromRemote( const Entry& e ) ;
|
void FromRemote( const Entry& e ) ;
|
||||||
|
|
|
@ -93,34 +93,12 @@ Json::Json( const std::vector<Json>& arr ) :
|
||||||
Add( *i ) ;
|
Add( *i ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json Json::Parse( const std::string& str )
|
template <>
|
||||||
|
Json::Json( const bool& b ) :
|
||||||
|
m_json( ::json_object_new_boolean( b ) )
|
||||||
{
|
{
|
||||||
struct json_object *json = ::json_tokener_parse( str.c_str() ) ;
|
if ( m_json == 0 )
|
||||||
if ( json == 0 )
|
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "cannot create json bool" ) ) ;
|
||||||
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "json parse error" ) ) ;
|
|
||||||
|
|
||||||
return Json( json, NotOwned() ) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json Json::ParseFile( const std::string& filename )
|
|
||||||
{
|
|
||||||
StdioFile file( filename ) ;
|
|
||||||
struct json_tokener *tok = ::json_tokener_new() ;
|
|
||||||
|
|
||||||
struct json_object *json = 0 ;
|
|
||||||
|
|
||||||
char buf[1024] ;
|
|
||||||
std::size_t count = 0 ;
|
|
||||||
|
|
||||||
while ( (count = file.Read( buf, sizeof(buf) ) ) > 0 )
|
|
||||||
json = ::json_tokener_parse_ex( tok, buf, count ) ;
|
|
||||||
|
|
||||||
if ( json == 0 )
|
|
||||||
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( ::json_tokener_errors[tok->err] ) ) ;
|
|
||||||
|
|
||||||
::json_tokener_free( tok ) ;
|
|
||||||
|
|
||||||
return Json( json, NotOwned() ) ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Json( struct json_object *json, NotOwned ) :
|
Json::Json( struct json_object *json, NotOwned ) :
|
||||||
|
@ -202,6 +180,20 @@ bool Json::Has( const std::string& key ) const
|
||||||
return ::json_object_object_get( m_json, key.c_str() ) != 0 ;
|
return ::json_object_object_get( m_json, key.c_str() ) != 0 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Json::Get( const std::string& key, Json& json ) const
|
||||||
|
{
|
||||||
|
assert( m_json != 0 ) ;
|
||||||
|
struct json_object *j = ::json_object_object_get( m_json, key.c_str() ) ;
|
||||||
|
if ( j != 0 )
|
||||||
|
{
|
||||||
|
Json tmp( j, NotOwned() ) ;
|
||||||
|
json.Swap( tmp ) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false ;
|
||||||
|
}
|
||||||
|
|
||||||
void Json::Add( const std::string& key, const Json& json )
|
void Json::Add( const std::string& key, const Json& json )
|
||||||
{
|
{
|
||||||
assert( m_json != 0 ) ;
|
assert( m_json != 0 ) ;
|
||||||
|
@ -341,4 +333,34 @@ bool Json::FindInArray( const std::string& key, const std::string& value, Json&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json Json::Parse( const std::string& str )
|
||||||
|
{
|
||||||
|
struct json_object *json = ::json_tokener_parse( str.c_str() ) ;
|
||||||
|
if ( json == 0 )
|
||||||
|
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "json parse error" ) ) ;
|
||||||
|
|
||||||
|
return Json( json, NotOwned() ) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json Json::ParseFile( const std::string& filename )
|
||||||
|
{
|
||||||
|
StdioFile file( filename ) ;
|
||||||
|
struct json_tokener *tok = ::json_tokener_new() ;
|
||||||
|
|
||||||
|
struct json_object *json = 0 ;
|
||||||
|
|
||||||
|
char buf[1024] ;
|
||||||
|
std::size_t count = 0 ;
|
||||||
|
|
||||||
|
while ( (count = file.Read( buf, sizeof(buf) ) ) > 0 )
|
||||||
|
json = ::json_tokener_parse_ex( tok, buf, count ) ;
|
||||||
|
|
||||||
|
if ( json == 0 )
|
||||||
|
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( ::json_tokener_errors[tok->err] ) ) ;
|
||||||
|
|
||||||
|
::json_tokener_free( tok ) ;
|
||||||
|
|
||||||
|
return Json( json, NotOwned() ) ;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ public :
|
||||||
bool Is() const ;
|
bool Is() const ;
|
||||||
|
|
||||||
bool Has( const std::string& key ) const ;
|
bool Has( const std::string& key ) const ;
|
||||||
|
bool Get( const std::string& key, Json& json ) const ;
|
||||||
void Add( const std::string& key, const Json& json ) ;
|
void Add( const std::string& key, const Json& json ) ;
|
||||||
void Add( const Json& json ) ;
|
void Add( const Json& json ) ;
|
||||||
Json FindInArray( const std::string& key, const std::string& value ) const ;
|
Json FindInArray( const std::string& key, const std::string& value ) const ;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "Assert.hh"
|
#include "Assert.hh"
|
||||||
|
|
||||||
#include "drive/State.hh"
|
#include "drive/State.hh"
|
||||||
|
#include "protocol/Json.hh"
|
||||||
#include "util/Log.hh"
|
#include "util/Log.hh"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -36,7 +37,7 @@ StateTest::StateTest( )
|
||||||
|
|
||||||
void StateTest::TestSync( )
|
void StateTest::TestSync( )
|
||||||
{
|
{
|
||||||
State s( TEST_DATA "/test_dir1.state" ) ;
|
State s( TEST_DATA "/test_dir1.state", Json() ) ;
|
||||||
Resource *r = s.Find( "./folder1/abc.txt" ) ;
|
Resource *r = s.Find( "./folder1/abc.txt" ) ;
|
||||||
CPPUNIT_ASSERT( r != 0 ) ;
|
CPPUNIT_ASSERT( r != 0 ) ;
|
||||||
GRUT_ASSERT_EQUAL( r->Name(), "abc.txt" ) ;
|
GRUT_ASSERT_EQUAL( r->Name(), "abc.txt" ) ;
|
||||||
|
@ -45,7 +46,6 @@ void StateTest::TestSync( )
|
||||||
|
|
||||||
// load directory
|
// load directory
|
||||||
s.FromLocal( TEST_DATA "/test_dir1" ) ;
|
s.FromLocal( TEST_DATA "/test_dir1" ) ;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end of namespace grut
|
} // end of namespace grut
|
||||||
|
|
Loading…
Reference in New Issue