1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::{database::*, StorageService};
use anyhow::Result;
use openvet_common::{
    rust::{CrateInfo, CrateName, VersionInfo},
    tree::Node,
};
use rusqlite::{named_params, Connection};

impl StorageService {
    pub async fn crate_sync(&self, info: CrateInfo) -> Result<()> {
        let changes = move |conn: &mut Connection| {
            conn.execute(
                "INSERT OR IGNORE INTO rust_crates(name) VALUES (?)",
                (&*info.metadata.name,),
            )?;

            for (_, version) in info.versions.iter() {
                conn.execute(
                    "INSERT INTO rust_crate_versions(crate, version, checksum, yanked)
                    VALUES (
                        (SELECT id FROM rust_crates WHERE name = :crate),
                        :version,
                        :checksum,
                        :yanked
                    )
                    ON CONFLICT(crate, version) DO UPDATE SET
                        yanked = :yanked,
                        checksum = :checksum
                    ",
                    named_params! {
                        ":crate": &*info.metadata.name,
                        ":version": &version.version.to_string(),
                        ":yanked": version.yanked,
                        ":checksum": version.checksum.to_string(),
                    },
                )?;
            }

            Ok(()) as Result<()>
        };
        let writer = self.database().write(changes).await??;
        Ok(())
    }

    pub async fn crate_files_write(&self, name: CrateName, files: Node) -> Result<()> {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use openvet_common::rust::CrateMetadata;

    #[tokio::test]
    async fn test_crate_sync_no_version() {
        let info = CrateInfo {
            metadata: CrateMetadata {
                name: "test-crate".parse().unwrap(),
            },
            versions: [].into(),
        };

        let service = StorageService::memory().await.unwrap();
        service.crate_sync(info.clone()).await.unwrap();
        service.crate_sync(info).await.unwrap();
    }

    #[tokio::test]
    async fn test_crate_sync_change_yanked() {
        let service = StorageService::memory().await.unwrap();
        let name = "test-crate".parse::<CrateName>().unwrap();
        let info = CrateInfo {
            metadata: CrateMetadata {
                name: "test-crate".parse().unwrap(),
            },
            versions: [(
                "0.1.0".parse().unwrap(),
                VersionInfo {
                    krate: name.clone(),
                    version: "0.1.0".parse().unwrap(),
                    checksum: Default::default(),
                    yanked: false,
                },
            )]
            .into(),
        };
        service.crate_sync(info).await.unwrap();

        let info = CrateInfo {
            metadata: CrateMetadata {
                name: "test-crate".parse().unwrap(),
            },
            versions: [(
                "0.1.0".parse().unwrap(),
                VersionInfo {
                    krate: name.clone(),
                    version: "0.1.0".parse().unwrap(),
                    checksum: Default::default(),
                    yanked: true,
                },
            )]
            .into(),
        };
        service.crate_sync(info).await.unwrap();
    }

    #[tokio::test]
    async fn test_crate_sync_versions() {
        let service = StorageService::memory().await.unwrap();
        let name = "test-crate".parse::<CrateName>().unwrap();
        let info = CrateInfo {
            metadata: CrateMetadata { name: name.clone() },
            versions: [].into(),
        };
        service.crate_sync(info).await.unwrap();

        let info = CrateInfo {
            metadata: CrateMetadata {
                name: "test-crate".parse().unwrap(),
            },
            versions: [(
                "0.1.0".parse().unwrap(),
                VersionInfo {
                    krate: name.clone(),
                    version: "0.1.0".parse().unwrap(),
                    checksum: Default::default(),
                    yanked: false,
                },
            )]
            .into(),
        };
        service.crate_sync(info).await.unwrap();
    }
}