Skip to main content

dnx_core/
package_json.rs

1use serde::{Deserialize, Deserializer, Serialize};
2use std::collections::HashMap;
3use std::fs;
4use std::path::Path;
5
6use super::errors::{DnxError, Result};
7
8pub type Dependencies = HashMap<String, String>;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PackageJson {
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub name: Option<String>,
14
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub version: Option<String>,
17
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub description: Option<String>,
20
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub main: Option<String>,
23
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub license: Option<String>,
26
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub dependencies: Option<Dependencies>,
29
30    #[serde(rename = "devDependencies", skip_serializing_if = "Option::is_none")]
31    pub dev_dependencies: Option<Dependencies>,
32
33    #[serde(rename = "peerDependencies", skip_serializing_if = "Option::is_none")]
34    pub peer_dependencies: Option<Dependencies>,
35
36    #[serde(
37        rename = "optionalDependencies",
38        skip_serializing_if = "Option::is_none"
39    )]
40    pub optional_dependencies: Option<Dependencies>,
41
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub scripts: Option<HashMap<String, String>>,
44
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub bin: Option<BinField>,
47
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub engines: Option<HashMap<String, String>>,
50
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub private: Option<bool>,
53
54    /// npm-style overrides: force specific versions of transitive dependencies.
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub overrides: Option<HashMap<String, String>>,
57
58    /// yarn-style resolutions: force specific versions of transitive dependencies.
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub resolutions: Option<HashMap<String, String>>,
61
62    /// Workspace member patterns (e.g. ["packages/*", "apps/*"]).
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub workspaces: Option<Vec<String>>,
65}
66
67#[derive(Debug, Clone, Serialize)]
68#[serde(untagged)]
69pub enum BinField {
70    Single(String),
71    Map(HashMap<String, String>),
72}
73
74impl<'de> Deserialize<'de> for BinField {
75    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
76    where
77        D: Deserializer<'de>,
78    {
79        #[derive(Deserialize)]
80        #[serde(untagged)]
81        enum BinFieldHelper {
82            Single(String),
83            Map(HashMap<String, String>),
84        }
85
86        match BinFieldHelper::deserialize(deserializer)? {
87            BinFieldHelper::Single(s) => Ok(BinField::Single(s)),
88            BinFieldHelper::Map(m) => Ok(BinField::Map(m)),
89        }
90    }
91}
92
93impl PackageJson {
94    pub fn read(path: &Path) -> Result<Self> {
95        let content = fs::read_to_string(path).map_err(|e| {
96            DnxError::Io(format!(
97                "Failed to read package.json at {}: {}",
98                path.display(),
99                e
100            ))
101        })?;
102
103        let package_json: PackageJson = serde_json::from_str(&content).map_err(|e| {
104            DnxError::ParseError(format!(
105                "Failed to parse package.json at {}: {}",
106                path.display(),
107                e
108            ))
109        })?;
110
111        Ok(package_json)
112    }
113
114    pub fn write(&self, path: &Path) -> Result<()> {
115        let content = serde_json::to_string_pretty(self).map_err(|e| {
116            DnxError::ParseError(format!("Failed to serialize package.json: {}", e))
117        })?;
118
119        fs::write(path, content).map_err(|e| {
120            DnxError::Io(format!(
121                "Failed to write package.json to {}: {}",
122                path.display(),
123                e
124            ))
125        })?;
126
127        Ok(())
128    }
129
130    pub fn all_dependencies(&self) -> HashMap<String, String> {
131        let mut all_deps = HashMap::new();
132
133        if let Some(deps) = &self.dependencies {
134            all_deps.extend(deps.clone());
135        }
136
137        if let Some(dev_deps) = &self.dev_dependencies {
138            all_deps.extend(dev_deps.clone());
139        }
140
141        all_deps
142    }
143}