• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2025 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::{
16     collections::BTreeMap,
17     fs::{read_to_string, write},
18     path::Path,
19 };
20 
21 use anyhow::Result;
22 use chrono::{DateTime, Days, Local};
23 use serde::{Deserialize, Serialize};
24 
25 #[derive(Serialize, Deserialize, Default)]
26 pub struct UpdatesTried {
27     attempts: BTreeMap<String, CrateUpdate>,
28 }
29 
30 #[derive(Serialize, Deserialize)]
31 struct CrateUpdate {
32     name: String,
33     version: String,
34     time: DateTime<Local>,
35     success: bool,
36 }
37 
38 impl UpdatesTried {
39     /// Read updates-tried.json and prune old entries.
40     // TODO: Put this somewhere better than $CWD.
read() -> Result<Self>41     pub fn read() -> Result<Self> {
42         let mut parsed: UpdatesTried = if Path::new("updates-tried.json").exists() {
43             serde_json::from_str(read_to_string("updates-tried.json")?.as_str())?
44         } else {
45             UpdatesTried::default()
46         };
47         let now = chrono::Local::now();
48         parsed.attempts.retain(|_, u| u.time.checked_add_days(Days::new(14)).unwrap_or(now) > now);
49         Ok(parsed)
50     }
write(&self) -> Result<()>51     pub fn write(&self) -> Result<()> {
52         let mut contents = serde_json::to_string_pretty(self)?;
53         contents.push('\n');
54         write("updates-tried.json", contents)?;
55         Ok(())
56     }
contains(&self, name: &str, version: &str) -> bool57     pub fn contains(&self, name: &str, version: &str) -> bool {
58         self.attempts.contains_key(&key(name, version))
59     }
record(&mut self, name: String, version: String, success: bool) -> Result<()>60     pub fn record(&mut self, name: String, version: String, success: bool) -> Result<()> {
61         self.attempts.insert(
62             key(&name, &version),
63             CrateUpdate { name, version, time: chrono::Local::now(), success },
64         );
65         self.write()
66     }
67 }
68 
key(name: &str, version: &str) -> String69 fn key(name: &str, version: &str) -> String {
70     format!("{name}-v{version}")
71 }
72