pkglock_lib/
lib.rs

1
2// New module to encapsulate the reading, parsing, and updating of package-lock.json, as well as the URL update functionality
3pub mod package_lock_lib {
4    use regex::Regex;
5    use serde_json::Value;
6    use std::fs;
7    use std::path::Path;
8
9    // Function to update URLs in a JSON value recursively
10    pub fn update_urls(value: &mut Value, new_url: &str) {
11        if let Value::Object(map) = value {
12            if let Some(resolved) = map.get_mut("resolved") {
13                if resolved.is_string() {
14                    let old_url = resolved.as_str().unwrap();
15                    // Create a regex to match URLs
16                    let re = Regex::new(r"https?://[^/]+").unwrap();
17                    // Replace the matched part of the URL with the new URL
18                    let updated_url = re.replace(old_url, new_url);
19                    *resolved = Value::String(updated_url.into_owned());
20                }
21            }
22            // Recursively update nested objects
23            for key in map.keys().cloned().collect::<Vec<_>>() {
24                update_urls(&mut map[&key], new_url);
25            }
26        }
27    }
28
29    // Function that reads pkg.config.json and package-lock.json, updates the package-lock.json with the new URL,
30    // and writes the updated JSON back to package-lock.json
31    pub fn update_urls_in_package_lock(arg: &str) -> Result<(), Box<dyn std::error::Error>> {
32        // Check that the required files exist
33        if !Path::new("pkg.config.json").exists() {
34            return Err("pkg.config.json not found".into());
35        }
36        if !Path::new("package-lock.json").exists() {
37            return Err("package-lock.json not found".into());
38        }
39
40        // Read and parse package-lock.json
41        let file_content = fs::read_to_string("package-lock.json")?;
42        let mut json_content: Value = serde_json::from_str(&file_content)?;
43
44        // Read and parse pkg.config.json
45        let config_content = fs::read_to_string("pkg.config.json")?;
46        let config: Value = serde_json::from_str(&config_content)?;
47
48        // Determine new URL based on argument
49        let new_url = if arg == "--local" {
50            config["local"].as_str().ok_or("Local URL not found in pkg.config.json")?
51        } else if arg == "--remote" {
52            config["remote"].as_str().ok_or("Remote URL not found in pkg.config.json")?
53        } else {
54            return Err("Invalid argument. Use --local or --remote.".into());
55        };
56
57        // Update URLs using the update_urls function within this module
58        update_urls(&mut json_content, new_url);
59
60        // Write the updated JSON back to package-lock.json
61        let updated_content = serde_json::to_string_pretty(&json_content)?;
62        fs::write("package-lock.json", updated_content)?;
63        Ok(())
64    }
65
66    #[cfg(test)]
67    mod tests {
68        use super::*;
69        use serde_json::json;
70        use std::fs;
71
72        #[test]
73        fn test_update_urls_simple() {
74            let mut json = json!({
75                "resolved": "https://registry.npmjs.org/package/-/package-1.0.0.tgz"
76            });
77            update_urls(&mut json, "http://localhost:4873");
78            assert_eq!(
79                json["resolved"],
80                "http://localhost:4873/package/-/package-1.0.0.tgz"
81            );
82        }
83
84        #[test]
85        fn test_update_urls_nested() {
86            let mut json = json!({
87                "dependencies": {
88                    "package": {
89                        "resolved": "https://registry.npmjs.org/package/-/package-1.0.0.tgz"
90                    }
91                }
92            });
93            update_urls(&mut json, "http://localhost:4873");
94            assert_eq!(
95                json["dependencies"]["package"]["resolved"],
96                "http://localhost:4873/package/-/package-1.0.0.tgz"
97            );
98        }
99
100        #[test]
101        fn test_update_urls_no_resolved_field() {
102            let mut json = json!({
103                "name": "test-package",
104                "version": "1.0.0"
105            });
106            update_urls(&mut json, "http://localhost:4873");
107            assert_eq!(json["name"], "test-package");
108            assert_eq!(json["version"], "1.0.0");
109        }
110
111        #[test]
112        fn test_update_urls_in_package_lock() {
113            // Create temporary pkg.config.json
114            let pkg_config = r#"{
115                "local": "http://localhost:4873",
116                "remote": "https://registry.npmjs.org"
117            }"#;
118            fs::write("pkg.config.json", pkg_config).unwrap();
119
120            // Create temporary package-lock.json
121            let package_lock = r#"{
122                "dependencies": {
123                    "package-a": {
124                        "resolved": "https://registry.npmjs.org/package-a/-/package-a-1.0.0.tgz"
125                    }
126                }
127            }"#;
128            fs::write("package-lock.json", package_lock).unwrap();
129
130            // Call the function with --local argument
131            update_urls_in_package_lock("--local").unwrap();
132
133            // Read updated package-lock.json
134            let updated_content = fs::read_to_string("package-lock.json").unwrap();
135            assert!(updated_content.contains("http://localhost:4873"));
136            assert!(!updated_content.contains("https://registry.npmjs.org"));
137
138            // Clean up temporary files
139            fs::remove_file("pkg.config.json").unwrap();
140            fs::remove_file("package-lock.json").unwrap();
141        }
142    }
143}