user_dependencies_graph/
user-dependencies-graph.rs

1//! $ cargo run --release --example user-dependencies2
2//!
3//! Computes time series of the fraction of deps on crates.io which point to a
4//! given user's crates.
5
6use std::collections::{BTreeMap as Map, BTreeSet as Set};
7
8const USER: &str = "dtolnay";
9
10fn main() -> db_dump::Result<()> {
11    let mut user_id = None;
12    let mut crate_owners = Vec::new();
13    let mut dependencies = Map::new();
14    let mut versions = Vec::new();
15    db_dump::Loader::new()
16        .users(|row| {
17            if row.gh_login == USER {
18                user_id = Some(row.id);
19            }
20        })
21        .crate_owners(|row| crate_owners.push(row))
22        .dependencies(|row| {
23            dependencies
24                .entry(row.version_id)
25                .or_insert_with(Vec::new)
26                .push(row);
27        })
28        .versions(|row| {
29            if !row.yanked {
30                versions.push(row);
31            }
32        })
33        .load("./db-dump.tar.gz")?;
34
35    // User id of the crate author we care about.
36    let user_id = user_id.expect("no such user");
37
38    // Set of crate ids currently owned by that user.
39    let mut their_crates = Set::new();
40    for crate_owner in crate_owners {
41        if crate_owner.owner_id == user_id {
42            their_crates.insert(crate_owner.crate_id);
43        }
44    }
45
46    let mut total_deps = 0usize;
47    let mut their_deps = 0usize;
48    let mut last_printed_ratio = 0.0..=0.0;
49    let mut latest_version = Map::new();
50
51    versions.sort_by_key(|v| v.created_at);
52
53    for version in versions {
54        let no_deps = Vec::new();
55        if let Some(prev) = latest_version.insert(version.crate_id, version.id) {
56            for dep in dependencies.get(&prev).unwrap_or(&no_deps) {
57                total_deps -= 1;
58                their_deps -= their_crates.contains(&dep.crate_id) as usize;
59            }
60        }
61        for dep in dependencies.get(&version.id).unwrap_or(&no_deps) {
62            total_deps += 1;
63            their_deps += their_crates.contains(&dep.crate_id) as usize;
64        }
65        if total_deps != 0 {
66            let ratio = their_deps as f64 / total_deps as f64;
67            if !last_printed_ratio.contains(&ratio) {
68                println!("{},{:.3}", version.created_at.naive_utc(), ratio * 100.0);
69                last_printed_ratio = ratio * 0.99999..=ratio * 1.00001;
70            }
71        }
72    }
73
74    eprintln!(
75        "{} / {} ({:.02}%)",
76        their_deps,
77        total_deps,
78        (their_deps as f64 / total_deps as f64) * 100.0,
79    );
80    Ok(())
81}