@@ -4,17 +4,25 @@ use std::{
4
4
sync:: { Arc , Mutex , RwLockReadGuard } ,
5
5
} ;
6
6
7
+ use nu_ansi_term:: Color :: { Blue , Green , Magenta , Red } ;
7
8
use once_cell:: sync:: OnceCell ;
8
- use rusqlite:: Connection ;
9
+ use rusqlite:: { params , Connection } ;
9
10
use soar_core:: {
10
- config:: { get_config, Config } ,
11
+ config:: { get_config, Config , Repository } ,
11
12
constants:: CORE_MIGRATIONS ,
12
- database:: { connection:: Database , migration:: MigrationManager } ,
13
+ database:: {
14
+ connection:: Database ,
15
+ migration:: MigrationManager ,
16
+ models:: FromRow ,
17
+ packages:: { FilterCondition , PackageQueryBuilder } ,
18
+ } ,
13
19
error:: { ErrorContext , SoarError } ,
14
20
metadata:: fetch_metadata,
15
21
SoarResult ,
16
22
} ;
17
- use tracing:: error;
23
+ use tracing:: { error, info} ;
24
+
25
+ use crate :: utils:: Colored ;
18
26
19
27
#[ derive( Clone ) ]
20
28
pub struct AppState {
@@ -50,20 +58,103 @@ impl AppState {
50
58
for repo in & self . inner . config . repositories {
51
59
let repo_clone = repo. clone ( ) ;
52
60
let task = tokio:: task:: spawn ( async move { fetch_metadata ( repo_clone, force) . await } ) ;
53
- tasks. push ( task) ;
61
+ tasks. push ( ( task, repo ) ) ;
54
62
}
55
63
56
- for task in tasks {
57
- if let Err ( err ) = task
64
+ for ( task, repo ) in tasks {
65
+ match task
58
66
. await
59
67
. map_err ( |err| SoarError :: Custom ( format ! ( "Join handle error: {}" , err) ) ) ?
60
68
{
61
- if !matches ! ( err, SoarError :: FailedToFetchRemote ( _) ) {
62
- return Err ( err) ;
69
+ Ok ( Some ( etag) ) => {
70
+ self . validate_packages ( & repo, & etag) . await ?;
71
+ info ! ( "[{}] Repository synced" , Colored ( Magenta , & repo. name) ) ;
72
+ }
73
+ Err ( err) => {
74
+ if !matches ! ( err, SoarError :: FailedToFetchRemote ( _) ) {
75
+ return Err ( err) ;
76
+ }
77
+ error ! ( "{err}" ) ;
63
78
}
64
- error ! ( "{err}" ) ;
79
+ _ => { }
65
80
} ;
66
81
}
82
+
83
+ Ok ( ( ) )
84
+ }
85
+
86
+ async fn validate_packages ( & self , repo : & Repository , etag : & str ) -> SoarResult < ( ) > {
87
+ let core_db = self . core_db ( ) ?;
88
+ let repo_name = repo. name . clone ( ) ;
89
+
90
+ let repo_path = repo. get_path ( ) ?;
91
+ let metadata_db = repo_path. join ( "metadata.db" ) ;
92
+
93
+ let repo_db = Arc :: new ( Mutex :: new ( Connection :: open ( & metadata_db) ?) ) ;
94
+
95
+ let installed_packages = PackageQueryBuilder :: new ( core_db. clone ( ) )
96
+ . where_and ( "repo_name" , FilterCondition :: Eq ( repo_name. to_string ( ) ) )
97
+ . load_installed ( ) ?;
98
+
99
+ struct RepoPackage {
100
+ pkg_id : String ,
101
+ }
102
+
103
+ impl FromRow for RepoPackage {
104
+ fn from_row ( row : & rusqlite:: Row ) -> rusqlite:: Result < Self > {
105
+ Ok ( Self {
106
+ pkg_id : row. get ( "pkg_id" ) ?,
107
+ } )
108
+ }
109
+ }
110
+
111
+ for pkg in installed_packages. items {
112
+ let repo_package: Vec < RepoPackage > = PackageQueryBuilder :: new ( repo_db. clone ( ) )
113
+ . select ( & [ "pkg_id" ] )
114
+ . where_and ( "pkg_id" , FilterCondition :: Eq ( pkg. pkg_id . clone ( ) ) )
115
+ . where_and ( "repo_name" , FilterCondition :: Eq ( pkg. repo_name . clone ( ) ) )
116
+ . load ( ) ?
117
+ . items ;
118
+
119
+ if repo_package. is_empty ( ) {
120
+ let replaced_by: Vec < RepoPackage > = PackageQueryBuilder :: new ( repo_db. clone ( ) )
121
+ . select ( & [ "pkg_id" ] )
122
+ . where_and ( "repo_name" , FilterCondition :: Eq ( pkg. repo_name ) )
123
+ // there's no easy way to do this, could create scalar SQL
124
+ // function, but this is enough for now
125
+ . where_and (
126
+ & format ! ( "EXISTS (SELECT 1 FROM json_each(p.replaces) WHERE json_each.value = '{}')" , pkg. pkg_id) ,
127
+ FilterCondition :: None ,
128
+ )
129
+ . limit ( 1 )
130
+ . load ( ) ?
131
+ . items ;
132
+
133
+ if !replaced_by. is_empty ( ) {
134
+ let new_pkg_id = & replaced_by. first ( ) . unwrap ( ) . pkg_id ;
135
+ info ! (
136
+ "[{}] {} is replaced by {} in {}" ,
137
+ Colored ( Blue , "Note" ) ,
138
+ Colored ( Red , & pkg. pkg_id) ,
139
+ Colored ( Green , new_pkg_id) ,
140
+ Colored ( Magenta , & repo_name)
141
+ ) ;
142
+
143
+ let conn = core_db. lock ( ) ?;
144
+ conn. execute (
145
+ "UPDATE packages SET pkg_id = ? WHERE pkg_id = ? AND repo_name = ?" ,
146
+ params ! [ new_pkg_id, pkg. pkg_id, repo_name] ,
147
+ ) ?;
148
+ }
149
+ }
150
+ }
151
+
152
+ let conn = repo_db. lock ( ) ?;
153
+ conn. execute (
154
+ "UPDATE repository SET name = ?, etag = ?" ,
155
+ params ! [ repo. name, etag] ,
156
+ ) ?;
157
+
67
158
Ok ( ( ) )
68
159
}
69
160
0 commit comments