examples_relevancy_cli/
main.rs
1use std::sync::Arc;
6
7use clap::Parser;
8use cli_support::{
9 fxa_creds::{get_cli_fxa, get_default_fxa_config, SYNC_SCOPE},
10 remote_settings_service,
11};
12use env_logger::Builder;
13use interrupt_support::NeverInterrupts;
14use places::{ConnectionType, PlacesApi};
15use relevancy::RelevancyStore;
16use sync15::client::{sync_multiple, MemoryCachedState};
17use sync15::engine::SyncEngineId;
18
19use anyhow::{bail, Result};
20
21static CREDENTIALS_PATH: &str = ".cli-data/credentials.json";
22
23#[derive(Parser)]
24#[command(about, long_about = None)]
25struct Cli {
26 #[clap(long, short, action)]
28 verbose: bool,
29
30 #[clap(long)]
34 places_db: Option<String>,
35}
36
37fn main() -> Result<()> {
38 let cli = Cli::parse();
39 nss::ensure_initialized();
40 viaduct_reqwest::use_reqwest_backend();
41 if let Some(dir) = std::path::PathBuf::from(CREDENTIALS_PATH).parent() {
42 std::fs::create_dir_all(dir)?;
43 }
44 let mut builder = Builder::new();
45 builder.filter_level(log::LevelFilter::Info);
46 if cli.verbose {
47 builder.filter_module("relevancy", log::LevelFilter::Trace);
48 }
49 builder.init();
50 println!("================== Initializing Relevancy ===================");
51 let relevancy_store = RelevancyStore::new(
52 "file:relevancy-cli-relevancy?mode=memory&cache=shared".to_owned(),
53 remote_settings_service(),
54 );
55 relevancy_store.ensure_interest_data_populated()?;
56
57 println!("==================== Downloading History ====================");
58 let places_api = if let Some(path) = cli.places_db {
59 PlacesApi::new(path)?
60 } else {
61 let places_api = PlacesApi::new_memory("relevancy-cli-places")?;
62 sync_places(&places_api)?;
63 places_api
64 };
65
66 let conn = places_api.open_connection(ConnectionType::ReadOnly)?;
67 let top_frecency_info = places::storage::history::get_top_frecent_site_infos(&conn, 5000, 0)?;
68 let top_frecency_urls = top_frecency_info
69 .into_iter()
70 .map(|info| info.url.to_string())
71 .collect();
72 println!("==================== Calculated Interests====================");
73 let interest_vector = relevancy_store.ingest(top_frecency_urls)?;
74 interest_vector.print_all_counts();
75
76 Ok(())
77}
78
79fn sync_places(places_api: &Arc<PlacesApi>) -> Result<()> {
80 Arc::clone(places_api).register_with_sync_manager();
81 let cli_fxa = get_cli_fxa(get_default_fxa_config(), CREDENTIALS_PATH, &[SYNC_SCOPE])?;
82 let mut mem_cached_state = MemoryCachedState::default();
83 let mut global_state: Option<String> = None;
84 let engine = places::get_registered_sync_engine(&SyncEngineId::History)
85 .expect("no registered sync engine");
86 let result = sync_multiple(
87 &[&*engine],
88 &mut global_state,
89 &mut mem_cached_state,
90 &cli_fxa.client_init.clone(),
91 &cli_fxa.as_key_bundle()?,
92 &NeverInterrupts,
93 None,
94 );
95
96 if result.engine_results.len() != 1 {
97 bail!(
98 "Unexpected number of engine result: {}",
99 result.engine_results.len()
100 );
101 }
102
103 match result.result {
104 Ok(()) => log::info!("Sync successful"),
105 Err(e) => {
106 log::info!("Sync failed");
107 bail!(e);
108 }
109 }
110 Ok(())
111}