Cache file checksums and modification times in local index (in JSON in .grive_state)

syncer-move-dates
Vitaliy Filippov 2016-01-01 02:27:21 +03:00
parent 5381919e5b
commit 4edff0a816
14 changed files with 214 additions and 91 deletions

View File

@ -62,11 +62,11 @@ void Drive::FromRemote( const Entry& entry )
// so these checkings are done in normal entries only // so these checkings are done in normal entries only
Resource *parent = m_state.FindByHref( entry.ParentHref() ) ; Resource *parent = m_state.FindByHref( entry.ParentHref() ) ;
if ( parent != 0 && !parent->IsFolder() ) if ( parent && !parent->IsFolder() )
Log( "warning: entry %1% has parent %2% which is not a folder, ignored", Log( "warning: entry %1% has parent %2% which is not a folder, ignored",
entry.Title(), parent->Name(), log::verbose ) ; entry.Title(), parent->Name(), log::verbose ) ;
else if ( parent == 0 || !parent->IsInRootTree() ) else if ( !parent || !parent->IsInRootTree() )
Log( "file \"%1%\" parent doesn't exist, ignored", entry.Title(), log::verbose ) ; Log( "file \"%1%\" parent doesn't exist, ignored", entry.Title(), log::verbose ) ;
else else

View File

@ -48,8 +48,9 @@ Resource::Resource( const fs::path& root_folder ) :
m_id ( "folder:root" ), m_id ( "folder:root" ),
m_href ( "root" ), m_href ( "root" ),
m_is_editable( true ), m_is_editable( true ),
m_parent ( NULL ), m_parent ( 0 ),
m_state ( sync ) m_state ( sync ),
m_json ( NULL )
{ {
} }
@ -57,8 +58,9 @@ Resource::Resource( const std::string& name, const std::string& kind ) :
m_name ( name ), m_name ( name ),
m_kind ( kind ), m_kind ( kind ),
m_is_editable( true ), m_is_editable( true ),
m_parent ( NULL ), m_parent ( 0 ),
m_state ( unknown ) m_state ( unknown ),
m_json ( NULL )
{ {
} }
@ -137,10 +139,8 @@ void Resource::FromRemote( const Entry& remote, const DateTime& last_change )
assert( m_state != unknown ) ; assert( m_state != unknown ) ;
if ( m_state == remote_new || m_state == remote_changed ) if ( m_state == remote_new || m_state == remote_changed )
{ m_md5 = remote.MD5() ;
m_md5 = remote.MD5() ; m_mtime = remote.MTime() ;
m_mtime = remote.MTime() ;
}
} }
void Resource::AssignIDs( const Entry& remote ) void Resource::AssignIDs( const Entry& remote )
@ -233,15 +233,40 @@ void Resource::FromRemoteFile( const Entry& remote, const DateTime& last_change
/// Update the resource with the attributes of local file or directory. This /// Update the resource with the attributes of local file or directory. This
/// function will propulate the fields in m_entry. /// function will propulate the fields in m_entry.
void Resource::FromLocal( const DateTime& last_sync ) void Resource::FromLocal( const DateTime& last_sync, Val& state )
{ {
fs::path path = Path() ; assert( !m_json );
//assert( fs::exists( path ) ) ; m_json = &state;
// root folder is always in sync // root folder is always in sync
if ( !IsRoot() ) if ( !IsRoot() )
{ {
m_mtime = os::FileCTime( path ) ; fs::path path = Path() ;
bool is_dir;
os::Stat( path, &m_ctime, &m_size, &is_dir ) ;
m_name = path.filename().string() ;
if ( !is_dir )
{
m_kind = "file";
if ( state.Has( "ctime" ) && state.Has( "md5" ) && (u64_t) m_ctime.Sec() == state["ctime"].U64() )
m_md5 = state["md5"];
else
{
m_md5 = crypt::MD5::Get( path );
state.Set( "md5", Val( m_md5 ) );
state.Set( "ctime", Val( m_ctime.Sec() ) );
}
if ( state.Has( "srv_time" ) && m_mtime != DateTime() )
m_mtime.Assign( state[ "srv_time" ].U64(), 0 ) ;
}
else
{
m_kind = "folder";
state.Del( "md5" );
state.Del( "ctime" );
state.Del( "srv_time" );
}
// follow parent recursively // follow parent recursively
if ( m_parent->m_state == local_new || m_parent->m_state == local_deleted ) if ( m_parent->m_state == local_new || m_parent->m_state == local_deleted )
@ -251,11 +276,7 @@ void Resource::FromLocal( const DateTime& last_sync )
// remote_deleted first, it will be updated to sync/remote_changed // remote_deleted first, it will be updated to sync/remote_changed
// in FromRemote() // in FromRemote()
else else
m_state = ( m_mtime > last_sync ? local_new : remote_deleted ) ; m_state = ( m_ctime > last_sync ? local_new : remote_deleted ) ;
m_name = path.filename().string() ;
m_kind = IsFolder() ? "folder" : "file" ;
m_md5 = IsFolder() ? "" : crypt::MD5::Get( path ) ;
} }
assert( m_state != unknown ) ; assert( m_state != unknown ) ;
@ -286,7 +307,7 @@ std::string Resource::Kind() const
return m_kind ; return m_kind ;
} }
DateTime Resource::MTime() const DateTime Resource::ServerTime() const
{ {
return m_mtime ; return m_mtime ;
} }
@ -395,8 +416,26 @@ void Resource::Sync( Syncer *syncer, DateTime& sync_time, const Val& options )
// if myself is deleted, no need to do the childrens // if myself is deleted, no need to do the childrens
if ( m_state != local_deleted && m_state != remote_deleted ) if ( m_state != local_deleted && m_state != remote_deleted )
{
std::for_each( m_child.begin(), m_child.end(), std::for_each( m_child.begin(), m_child.end(),
boost::bind( &Resource::Sync, _1, syncer, boost::ref(sync_time), options ) ) ; boost::bind( &Resource::Sync, _1, syncer, boost::ref(sync_time), options ) ) ;
if ( IsFolder() )
{
// delete state of removed files
Val& tree = (*m_json)["tree"];
Val::Object leftover = tree.AsObject();
for( iterator i = m_child.begin(); i != m_child.end(); i++ )
{
Resource *r = *i;
if ( r->m_state != local_deleted && r->m_state != remote_deleted )
leftover.erase( r->Name() );
else
r->m_json = NULL;
}
for( Val::Object::iterator i = leftover.begin(); i != leftover.end(); i++ )
tree.Del( i->first );
}
}
} }
void Resource::SyncSelf( Syncer* syncer, const Val& options ) void Resource::SyncSelf( Syncer* syncer, const Val& options )
@ -438,7 +477,7 @@ void Resource::SyncSelf( Syncer* syncer, const Val& options )
fs::create_directories( path ) ; fs::create_directories( path ) ;
else else
syncer->Download( this, path ) ; syncer->Download( this, path ) ;
SetIndex() ;
m_state = sync ; m_state = sync ;
} }
break ; break ;
@ -449,6 +488,7 @@ void Resource::SyncSelf( Syncer* syncer, const Val& options )
if ( syncer ) if ( syncer )
{ {
syncer->Download( this, path ) ; syncer->Download( this, path ) ;
SetIndex() ;
m_state = sync ; m_state = sync ;
} }
break ; break ;
@ -462,7 +502,7 @@ void Resource::SyncSelf( Syncer* syncer, const Val& options )
case sync : case sync :
Log( "sync %1% already in sync", path, log::verbose ) ; Log( "sync %1% already in sync", path, log::verbose ) ;
break ; break ;
// shouldn't go here // shouldn't go here
case unknown : case unknown :
assert( false ) ; assert( false ) ;
@ -471,6 +511,17 @@ void Resource::SyncSelf( Syncer* syncer, const Val& options )
default : default :
break ; break ;
} }
if ( m_state != local_deleted && m_state != remote_deleted && !IsFolder() )
{
// Update server time of this file
m_json->Set( "srv_time", Val( m_mtime.Sec() ) );
}
}
void Resource::SetServerTime( const DateTime& time )
{
m_mtime = time ;
} }
/// this function doesn't really remove the local file. it renames it. /// this function doesn't really remove the local file. it renames it.
@ -478,7 +529,7 @@ void Resource::DeleteLocal()
{ {
static const boost::format trash_file( "%1%-%2%" ) ; static const boost::format trash_file( "%1%-%2%" ) ;
assert( m_parent != 0 ) ; assert( m_parent != NULL ) ;
Resource* p = m_parent; Resource* p = m_parent;
fs::path destdir; fs::path destdir;
while ( !p->IsRoot() ) while ( !p->IsRoot() )
@ -503,6 +554,20 @@ void Resource::DeleteLocal()
} }
} }
void Resource::SetIndex()
{
assert( m_parent->m_json != NULL );
if ( !m_json )
m_json = &((*m_parent->m_json)["tree"]).Item( Name() );
bool is_dir;
os::Stat( Path(), &m_ctime, &m_size, &is_dir );
if ( !is_dir )
{
m_json->Set( "ctime", Val( m_ctime.Sec() ) );
m_json->Set( "md5", Val( m_md5 ) );
}
}
Resource::iterator Resource::begin() const Resource::iterator Resource::begin() const
{ {
return m_child.begin() ; return m_child.begin() ;

View File

@ -87,7 +87,7 @@ public :
std::string Name() const ; std::string Name() const ;
std::string Kind() const ; std::string Kind() const ;
DateTime MTime() const ; DateTime ServerTime() const ;
std::string SelfHref() const ; std::string SelfHref() const ;
std::string ContentSrc() const ; std::string ContentSrc() const ;
std::string ETag() const ; std::string ETag() const ;
@ -107,9 +107,10 @@ public :
std::string MD5() const ; std::string MD5() const ;
void FromRemote( const Entry& remote, const DateTime& last_change ) ; void FromRemote( const Entry& remote, const DateTime& last_change ) ;
void FromLocal( const DateTime& last_sync ) ; void FromLocal( const DateTime& last_sync, Val& state ) ;
void Sync( Syncer* syncer, DateTime& sync_time, const Val& options ) ; void Sync( Syncer* syncer, DateTime& sync_time, const Val& options ) ;
void SetServerTime( const DateTime& time ) ;
// children access // children access
iterator begin() const ; iterator begin() const ;
@ -132,6 +133,7 @@ private :
void FromRemoteFile( const Entry& remote, const DateTime& last_change ) ; void FromRemoteFile( const Entry& remote, const DateTime& last_change ) ;
void DeleteLocal() ; void DeleteLocal() ;
void SetIndex() ;
void SyncSelf( Syncer* syncer, const Val& options ) ; void SyncSelf( Syncer* syncer, const Val& options ) ;
@ -140,6 +142,8 @@ private :
std::string m_kind ; std::string m_kind ;
std::string m_md5 ; std::string m_md5 ;
DateTime m_mtime ; DateTime m_mtime ;
DateTime m_ctime ;
off_t m_size ;
std::string m_id ; std::string m_id ;
std::string m_href ; std::string m_href ;

View File

@ -26,7 +26,6 @@
#include "util/Crypt.hh" #include "util/Crypt.hh"
#include "util/File.hh" #include "util/File.hh"
#include "util/log/Log.hh" #include "util/log/Log.hh"
#include "json/Val.hh"
#include "json/JsonParser.hh" #include "json/JsonParser.hh"
#include <fstream> #include <fstream>
@ -84,7 +83,10 @@ State::~State()
/// of local directory. /// of local directory.
void State::FromLocal( const fs::path& p ) void State::FromLocal( const fs::path& p )
{ {
FromLocal( p, m_res.Root() ) ; if ( !m_st.Has( "tree" ) )
m_st.Add( "tree", Val() );
m_res.Root()->FromLocal( m_last_sync, m_st ) ;
FromLocal( p, m_res.Root(), m_st["tree"] ) ;
} }
bool State::IsIgnore( const std::string& filename ) bool State::IsIgnore( const std::string& filename )
@ -92,44 +94,39 @@ bool State::IsIgnore( const std::string& filename )
return regex_search( filename.c_str(), m_ign_re ); return regex_search( filename.c_str(), m_ign_re );
} }
void State::FromLocal( const fs::path& p, Resource* folder ) void State::FromLocal( const fs::path& p, Resource* folder, Val& tree )
{ {
assert( folder != 0 ) ; assert( folder != 0 ) ;
assert( folder->IsFolder() ) ; assert( folder->IsFolder() ) ;
// sync the folder itself
folder->FromLocal( m_last_sync ) ;
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i ) for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
{ {
std::string fname = i->path().filename().string() ; std::string fname = i->path().filename().string() ;
fs::file_status st = fs::status(i->path());
std::string path = folder->IsRoot() ? fname : ( folder->RelPath() / fname ).string(); std::string path = folder->IsRoot() ? fname : ( folder->RelPath() / fname ).string();
if ( IsIgnore( path ) ) if ( IsIgnore( path ) )
Log( "file %1% is ignored by grive", path, log::verbose ) ; Log( "file %1% is ignored by grive", path, log::verbose ) ;
// check for broken symblic links
else if ( st.type() == fs::file_not_found )
Log( "file %1% doesn't exist (broken link?), ignored", i->path(), log::verbose ) ;
else else
{ {
bool is_dir = st.type() == fs::directory_file;
// if the Resource object of the child already exists, it should // if the Resource object of the child already exists, it should
// have been so no need to do anything here // have been so no need to do anything here
Resource *c = folder->FindChild( fname ) ; Resource *c = folder->FindChild( fname ) ;
if ( c == 0 ) if ( !c )
{ {
c = new Resource( fname, is_dir ? "folder" : "file" ) ; c = new Resource( fname, "" ) ;
folder->AddChild( c ) ; folder->AddChild( c ) ;
m_res.Insert( c ) ; m_res.Insert( c ) ;
} }
if ( !tree.Has( fname ) )
c->FromLocal( m_last_sync ) ; tree.Add( fname, Val() );
Val& rec = tree[fname];
if ( is_dir ) c->FromLocal( m_last_sync, rec ) ;
FromLocal( *i, c ) ; if ( c->IsFolder() )
{
if ( !rec.Has("tree") )
rec.Add( "tree", Val() );
FromLocal( *i, c, rec["tree"] ) ;
}
} }
} }
} }
@ -140,7 +137,7 @@ void State::FromRemote( const Entry& e )
std::string k = e.IsDir() ? "folder" : "file"; std::string k = e.IsDir() ? "folder" : "file";
// common checkings // common checkings
if ( !e.IsDir() && (fn.empty() || e.ContentSrc().empty()) ) if ( !e.IsDir() && ( fn.empty() || e.ContentSrc().empty() ) )
Log( "%1% \"%2%\" is a google document, ignored", k, e.Name(), log::verbose ) ; Log( "%1% \"%2%\" is a google document, ignored", k, e.Name(), log::verbose ) ;
else if ( fn.find('/') != fn.npos ) else if ( fn.find('/') != fn.npos )
@ -225,7 +222,7 @@ bool State::Update( const Entry& e )
// see if the entry already exist in local // see if the entry already exist in local
std::string name = e.Name() ; std::string name = e.Name() ;
Resource *child = parent->FindChild( name ) ; Resource *child = parent->FindChild( name ) ;
if ( child != 0 ) if ( child )
{ {
// since we are updating the ID and Href, we need to remove it and re-add it. // since we are updating the ID and Href, we need to remove it and re-add it.
m_res.Update( child, e, m_last_change ) ; m_res.Update( child, e, m_last_change ) ;
@ -273,21 +270,21 @@ void State::Read( const fs::path& filename )
{ {
File file( filename ) ; File file( filename ) ;
Val json = ParseJson( file ); m_st = ParseJson( file );
Val last_sync = json["last_sync"] ; Val last_sync = m_st["last_sync"] ;
Val last_change = json.Has( "last_change" ) ? json["last_change"] : json["last_sync"] ; Val last_change = m_st.Has( "last_change" ) ? m_st["last_change"] : m_st["last_sync"] ;
m_last_sync.Assign( last_sync["sec"].Int(), last_sync["nsec"].Int() ) ; m_last_sync.Assign( last_sync["sec"].Int(), last_sync["nsec"].Int() ) ;
m_last_change.Assign( last_change["sec"].Int(), last_change["nsec"].Int() ) ; m_last_change.Assign( last_change["sec"].Int(), last_change["nsec"].Int() ) ;
m_ign = json.Has( "ignore_regexp" ) ? json["ignore_regexp"].Str() : std::string(); m_ign = m_st.Has( "ignore_regexp" ) ? m_st["ignore_regexp"].Str() : std::string();
m_cstamp = json["change_stamp"].Int() ; m_cstamp = m_st["change_stamp"].Int() ;
} }
catch ( Exception& ) catch ( Exception& )
{ {
} }
} }
void State::Write( const fs::path& filename ) const void State::Write( const fs::path& filename )
{ {
Val last_sync ; Val last_sync ;
last_sync.Add( "sec", Val( (int)m_last_sync.Sec() ) ); last_sync.Add( "sec", Val( (int)m_last_sync.Sec() ) );
@ -297,14 +294,13 @@ void State::Write( const fs::path& filename ) const
last_change.Add( "sec", Val( (int)m_last_change.Sec() ) ); last_change.Add( "sec", Val( (int)m_last_change.Sec() ) );
last_change.Add( "nsec", Val( (unsigned)m_last_change.NanoSec() ) ); last_change.Add( "nsec", Val( (unsigned)m_last_change.NanoSec() ) );
Val result ; m_st.Set( "last_sync", last_sync ) ;
result.Add( "last_sync", last_sync ) ; m_st.Set( "last_change", last_change ) ;
result.Add( "last_change", last_change ) ; m_st.Set( "change_stamp", Val( m_cstamp ) ) ;
result.Add( "change_stamp", Val(m_cstamp) ) ; m_st.Set( "ignore_regexp", Val( m_ign ) ) ;
result.Add( "ignore_regexp", Val(m_ign) ) ;
std::ofstream fs( filename.string().c_str() ) ; std::ofstream fs( filename.string().c_str() ) ;
fs << result ; fs << m_st ;
} }
void State::Sync( Syncer *syncer, const Val& options ) void State::Sync( Syncer *syncer, const Val& options )

View File

@ -23,14 +23,13 @@
#include "util/DateTime.hh" #include "util/DateTime.hh"
#include "util/FileSystem.hh" #include "util/FileSystem.hh"
#include "json/Val.hh"
#include <memory> #include <memory>
#include <boost/regex.hpp> #include <boost/regex.hpp>
namespace gr { namespace gr {
class Val ;
class Entry ; class Entry ;
class Syncer ; class Syncer ;
@ -51,7 +50,7 @@ public :
void ResolveEntry() ; void ResolveEntry() ;
void Read( const fs::path& filename ) ; void Read( const fs::path& filename ) ;
void Write( const fs::path& filename ) const ; void Write( const fs::path& filename ) ;
Resource* FindByHref( const std::string& href ) ; Resource* FindByHref( const std::string& href ) ;
Resource* FindByID( const std::string& id ) ; Resource* FindByID( const std::string& id ) ;
@ -66,7 +65,7 @@ public :
bool Move( Syncer* syncer, fs::path old_p, fs::path new_p, fs::path grive_root ); bool Move( Syncer* syncer, fs::path old_p, fs::path new_p, fs::path grive_root );
private : private :
void FromLocal( const fs::path& p, Resource *folder ) ; void FromLocal( const fs::path& p, Resource *folder, Val& tree ) ;
void FromChange( const Entry& e ) ; void FromChange( const Entry& e ) ;
bool Update( const Entry& e ) ; bool Update( const Entry& e ) ;
std::size_t TryResolveEntry() ; std::size_t TryResolveEntry() ;
@ -80,6 +79,7 @@ private :
int m_cstamp ; int m_cstamp ;
std::string m_ign ; std::string m_ign ;
boost::regex m_ign_re ; boost::regex m_ign_re ;
Val m_st ;
std::vector<Entry> m_unresolved ; std::vector<Entry> m_unresolved ;
} ; } ;

View File

@ -44,8 +44,8 @@ void Syncer::Download( Resource *res, const fs::path& file )
long r = m_http->Get( res->ContentSrc(), &dl, http::Header() ) ; long r = m_http->Get( res->ContentSrc(), &dl, http::Header() ) ;
if ( r <= 400 ) if ( r <= 400 )
{ {
if ( res->MTime() != DateTime() ) if ( res->ServerTime() != DateTime() )
os::SetFileTime( file, res->MTime() ) ; os::SetFileTime( file, res->ServerTime() ) ;
else else
Log( "encountered zero date time after downloading %1%", file, log::warning ) ; Log( "encountered zero date time after downloading %1%", file, log::warning ) ;
} }
@ -56,9 +56,4 @@ void Syncer::AssignIDs( Resource *res, const Entry& remote )
res->AssignIDs( remote ); res->AssignIDs( remote );
} }
void Syncer::AssignMTime( Resource *res, const DateTime& mtime )
{
res->m_mtime = mtime;
}
} // end of namespace gr } // end of namespace gr

View File

@ -65,7 +65,6 @@ protected:
http::Agent *m_http; http::Agent *m_http;
void AssignIDs( Resource *res, const Entry& remote ); void AssignIDs( Resource *res, const Entry& remote );
void AssignMTime( Resource *res, const DateTime& mtime );
} ; } ;

View File

@ -232,7 +232,7 @@ bool Syncer1::Upload( Resource *res,
Log( "upload succeeded on retry", log::warning ); Log( "upload succeeded on retry", log::warning );
Entry1 responseEntry = Entry1( xml.Response() ); Entry1 responseEntry = Entry1( xml.Response() );
AssignIDs( res, responseEntry ) ; AssignIDs( res, responseEntry ) ;
AssignMTime( res, responseEntry.MTime() ); res->SetServerTime( responseEntry.MTime() );
break; break;
} }

View File

@ -201,7 +201,7 @@ bool Syncer2::Upload( Resource *res )
Entry2 responseEntry = Entry2( valr ) ; Entry2 responseEntry = Entry2( valr ) ;
AssignIDs( res, responseEntry ) ; AssignIDs( res, responseEntry ) ;
AssignMTime( res, responseEntry.MTime() ) ; res->SetServerTime( responseEntry.MTime() );
return true ; return true ;
} }

View File

@ -91,6 +91,18 @@ const Val& Val::operator[]( const std::string& key ) const
throw ; throw ;
} }
Val& Val::operator[]( const std::string& key )
{
Object& obj = As<Object>() ;
Object::iterator i = obj.find(key) ;
if ( i != obj.end() )
return i->second ;
// shut off compiler warning
BOOST_THROW_EXCEPTION(Error() << NoKey_(key)) ;
throw ;
}
const Val& Val::operator[]( std::size_t index ) const const Val& Val::operator[]( std::size_t index ) const
{ {
const Array& ar = As<Array>() ; const Array& ar = As<Array>() ;
@ -119,6 +131,13 @@ int Val::Int() const
return static_cast<int>(As<long long>()) ; return static_cast<int>(As<long long>()) ;
} }
unsigned long long Val::U64() const
{
if ( Type() == string_type )
return strtoull( As<std::string>().c_str(), NULL, 10 );
return static_cast<unsigned long long>(As<long long>()) ;
}
double Val::Double() const double Val::Double() const
{ {
if ( Type() == string_type ) if ( Type() == string_type )
@ -136,17 +155,38 @@ const Val::Array& Val::AsArray() const
return As<Array>() ; return As<Array>() ;
} }
Val::Array& Val::AsArray()
{
return As<Array>() ;
}
const Val::Object& Val::AsObject() const const Val::Object& Val::AsObject() const
{ {
return As<Object>() ; return As<Object>() ;
} }
Val::Object& Val::AsObject()
{
return As<Object>() ;
}
bool Val::Has( const std::string& key ) const bool Val::Has( const std::string& key ) const
{ {
const Object& obj = As<Object>() ; const Object& obj = As<Object>() ;
return obj.find(key) != obj.end() ; return obj.find(key) != obj.end() ;
} }
bool Val::Del( const std::string& key )
{
Object& obj = As<Object>() ;
return obj.erase(key) > 0 ;
}
Val& Val::Item( const std::string& key )
{
return As<Object>()[key];
}
bool Val::Get( const std::string& key, Val& val ) const bool Val::Get( const std::string& key, Val& val ) const
{ {
const Object& obj = As<Object>() ; const Object& obj = As<Object>() ;
@ -165,6 +205,16 @@ void Val::Add( const std::string& key, const Val& value )
As<Object>().insert( std::make_pair(key, value) ) ; As<Object>().insert( std::make_pair(key, value) ) ;
} }
void Val::Set( const std::string& key, const Val& value )
{
Object& obj = As<Object>();
Object::iterator i = obj.find(key);
if (i == obj.end())
obj.insert(std::make_pair(key, value));
else
i->second = value;
}
void Val::Add( const Val& json ) void Val::Add( const Val& json )
{ {
As<Array>().push_back( json ) ; As<Array>().push_back( json ) ;

View File

@ -94,24 +94,29 @@ public :
TypeEnum Type() const ; TypeEnum Type() const ;
const Val& operator[]( const std::string& key ) const ;
const Val& operator[]( std::size_t index ) const ;
// shortcuts for As<>() // shortcuts for As<>()
std::string Str() const ; std::string Str() const ;
int Int() const ; int Int() const ;
long Long() const ; unsigned long long U64() const ;
double Double() const ; double Double() const ;
bool Bool() const ; bool Bool() const ;
const Array& AsArray() const ; const Array& AsArray() const ;
Array& AsArray() ;
const Object& AsObject() const ; const Object& AsObject() const ;
Object& AsObject() ;
// shortcuts for objects // shortcuts for objects
bool Has( const std::string& key ) const ; Val& operator[]( const std::string& key ) ; // get updatable ref or throw
bool Get( const std::string& key, Val& val ) const ; const Val& operator[]( const std::string& key ) const ; // get const ref or throw
void Add( const std::string& key, const Val& val ) ; Val& Item( const std::string& key ) ; // insert if not exists and get
bool Has( const std::string& key ) const ; // check if exists
bool Get( const std::string& key, Val& val ) const ; // get or return false
void Add( const std::string& key, const Val& val ) ; // insert or do nothing
void Set( const std::string& key, const Val& val ) ; // insert or update
bool Del( const std::string& key ); // delete or do nothing
// shortcuts for array (and array of objects) // shortcuts for array (and array of objects)
const Val& operator[]( std::size_t index ) const ;
void Add( const Val& json ) ; void Add( const Val& json ) ;
std::vector<Val> Select( const std::string& key ) const ; std::vector<Val> Select( const std::string& key ) const ;
@ -191,7 +196,7 @@ const T& Val::As() const
{ {
try try
{ {
const Impl<T> *impl = &dynamic_cast<const Impl<T>&>( *m_base ) ; const Impl<T> *impl = dynamic_cast<const Impl<T> *>( m_base.get() ) ;
return impl->val ; return impl->val ;
} }
catch ( std::exception& e ) catch ( std::exception& e )
@ -208,7 +213,7 @@ T& Val::As()
{ {
try try
{ {
Impl<T> *impl = &dynamic_cast<Impl<T>&>( *m_base ) ; Impl<T> *impl = dynamic_cast<Impl<T> *>( m_base.get() ) ;
return impl->val ; return impl->val ;
} }
catch ( std::exception& e ) catch ( std::exception& e )

View File

@ -39,12 +39,12 @@
namespace gr { namespace os { namespace gr { namespace os {
DateTime FileCTime( const fs::path& filename ) void Stat( const fs::path& filename, DateTime *t, off_t *size, bool *is_dir )
{ {
return FileCTime( filename.string() ) ; Stat( filename.string(), t, size, is_dir ) ;
} }
DateTime FileCTime( const std::string& filename ) void Stat( const std::string& filename, DateTime *t, off_t *size, bool *is_dir )
{ {
struct stat s = {} ; struct stat s = {} ;
if ( ::stat( filename.c_str(), &s ) != 0 ) if ( ::stat( filename.c_str(), &s ) != 0 )
@ -57,11 +57,18 @@ DateTime FileCTime( const std::string& filename )
) ; ) ;
} }
if (t)
{
#if defined __APPLE__ && defined __DARWIN_64_BIT_INO_T #if defined __APPLE__ && defined __DARWIN_64_BIT_INO_T
return DateTime( s.st_ctimespec.tv_sec, s.st_ctimespec.tv_nsec ) ; *t = DateTime( s.st_ctimespec.tv_sec, s.st_ctimespec.tv_nsec ) ;
#else #else
return DateTime( s.st_ctim.tv_sec, s.st_ctim.tv_nsec); *t = DateTime( s.st_ctim.tv_sec, s.st_ctim.tv_nsec);
#endif #endif
}
if (size)
*size = s.st_size;
if (is_dir)
*is_dir = S_ISDIR(s.st_mode) ? true : false;
} }
void SetFileTime( const fs::path& filename, const DateTime& t ) void SetFileTime( const fs::path& filename, const DateTime& t )

View File

@ -33,8 +33,8 @@ namespace os
{ {
struct Error : virtual Exception {} ; struct Error : virtual Exception {} ;
DateTime FileCTime( const std::string& filename ) ; void Stat( const std::string& filename, DateTime *t, off_t *size, bool *is_dir ) ;
DateTime FileCTime( const fs::path& filename ) ; void Stat( const fs::path& filename, DateTime *t, off_t *size, bool *is_dir ) ;
void SetFileTime( const std::string& filename, const DateTime& t ) ; void SetFileTime( const std::string& filename, const DateTime& t ) ;
void SetFileTime( const fs::path& filename, const DateTime& t ) ; void SetFileTime( const fs::path& filename, const DateTime& t ) ;

View File

@ -25,6 +25,7 @@
#include "drive/Entry1.hh" #include "drive/Entry1.hh"
#include "xml/Node.hh" #include "xml/Node.hh"
#include "json/Val.hh"
#include <iostream> #include <iostream>
@ -53,7 +54,8 @@ void ResourceTest::TestNormal( )
GRUT_ASSERT_EQUAL( subject.Path(), fs::path( TEST_DATA ) / "entry.xml" ) ; GRUT_ASSERT_EQUAL( subject.Path(), fs::path( TEST_DATA ) / "entry.xml" ) ;
subject.FromLocal( DateTime() ) ; Val st;
subject.FromLocal( DateTime(), st ) ;
GRUT_ASSERT_EQUAL( subject.MD5(), "c0742c0a32b2c909b6f176d17a6992d0" ) ; GRUT_ASSERT_EQUAL( subject.MD5(), "c0742c0a32b2c909b6f176d17a6992d0" ) ;
GRUT_ASSERT_EQUAL( subject.StateStr(), "local_new" ) ; GRUT_ASSERT_EQUAL( subject.StateStr(), "local_new" ) ;