workspacer_crate/
crate_handle_serde.rs

1// ---------------- [ File: workspacer-crate/src/crate_handle_serde.rs ]
2crate::ix!();
3
4impl ::serde::Serialize for CrateHandle {
5    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
6    where
7        S: ::serde::Serializer,
8    {
9        use ::serde::ser::SerializeStruct;
10        use tracing::{trace, debug, error};
11
12        trace!("Serializing CrateHandle");
13
14        // Clone the Arc so the async closure can own it by value and not borrow `self`.
15        let cargo_toml_handle = self.cargo_toml_handle().clone();
16
17        let cargo_toml_raw = sync_run_async(async move {
18            trace!("Locking cargo_toml_handle in async block for serialization");
19            let guard = cargo_toml_handle.lock().await;
20            match toml::to_string(&*guard) {
21                Ok(s) => {
22                    debug!("Converted CargoToml to raw TOML string successfully for serialization");
23                    s
24                }
25                Err(e) => {
26                    error!("Failed to convert CargoToml to TOML string: {:?}", e);
27                    // You could return an Err(...) if you prefer. Here we just fallback to an empty string.
28                    String::new()
29                }
30            }
31        });
32
33        let mut state = serializer.serialize_struct("CrateHandle", 2)?;
34        state.serialize_field("crate_path", &self.crate_path())?;
35        state.serialize_field("cargo_toml_raw", &cargo_toml_raw)?;
36        state.end()
37    }
38}
39
40impl<'de> ::serde::Deserialize<'de> for CrateHandle {
41    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
42    where
43        D: ::serde::Deserializer<'de>,
44    {
45        use std::fmt;
46        use tracing::{trace, debug, error};
47
48        struct CrateHandleVisitor;
49
50        impl<'de> ::serde::de::Visitor<'de> for CrateHandleVisitor {
51            type Value = CrateHandle;
52
53            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54                formatter.write_str("struct CrateHandle with fields crate_path and cargo_toml_raw")
55            }
56
57            fn visit_map<V>(self, mut map: V) -> Result<CrateHandle, V::Error>
58            where
59                V: ::serde::de::MapAccess<'de>,
60            {
61                let mut crate_path_opt: Option<PathBuf> = None;
62                let mut cargo_toml_raw_opt: Option<String> = None;
63
64                while let Some(key) = map.next_key::<String>()? {
65                    match key.as_str() {
66                        "crate_path" => {
67                            crate_path_opt = Some(map.next_value()?);
68                        }
69                        "cargo_toml_raw" => {
70                            cargo_toml_raw_opt = Some(map.next_value()?);
71                        }
72                        _ => {
73                            let _ignored: ::serde::de::IgnoredAny = map.next_value()?;
74                        }
75                    }
76                }
77
78                let crate_path = crate_path_opt
79                    .ok_or_else(|| ::serde::de::Error::missing_field("crate_path"))?;
80                let cargo_toml_raw = cargo_toml_raw_opt
81                    .ok_or_else(|| ::serde::de::Error::missing_field("cargo_toml_raw"))?;
82
83                trace!("Deserializing raw TOML into CargoToml synchronously");
84                let cargo_toml: CargoToml = toml::from_str(&cargo_toml_raw).map_err(|e| {
85                    error!("Failed to parse cargo_toml_raw: {:?}", e);
86                    ::serde::de::Error::custom(format!("Could not parse cargo_toml_raw: {e}"))
87                })?;
88
89                debug!("Successfully reconstructed CargoToml from raw TOML");
90                let cargo_toml_handle = Arc::new(AsyncMutex::new(cargo_toml));
91
92                Ok(CrateHandleBuilder::default()
93                    .crate_path(crate_path)
94                    .cargo_toml_handle(cargo_toml_handle)
95                    .build()
96                    .unwrap()
97                )
98            }
99        }
100
101        deserializer.deserialize_struct(
102            "CrateHandle",
103            &["crate_path", "cargo_toml_raw"],
104            CrateHandleVisitor,
105        )
106    }
107}
108
109#[cfg(test)]
110mod test_crate_handle_serde {
111    use super::*;
112    use serde_json;
113
114    #[traced_test]
115    fn test_serialize_deserialize_crate_handle() {
116        use std::io::Write;
117        use tempfile::tempdir;
118
119        // 1) Make a minimal CargoToml so we can store some data in cargo_toml_handle
120        let tmp_dir = tempdir().unwrap();
121        let cargo_toml_path = tmp_dir.path().join("Cargo.toml");
122        let cargo_toml_content = r#"
123            [package]
124            name = "some_crate"
125            version = "0.1.2"
126            authors = ["Someone <someone@example.com>"]
127            license = "MIT"
128        "#;
129
130        {
131            let mut f = std::fs::File::create(&cargo_toml_path)
132                .expect("Failed to create Cargo.toml");
133            f.write_all(cargo_toml_content.as_bytes())
134                .expect("Failed to write Cargo.toml");
135        }
136
137        // 2) Create the CargoToml in a synchronous manner just to simulate existing data
138        let cargo_toml = CargoToml::new_sync(cargo_toml_path)
139            .expect("Failed to load test Cargo.toml via new_sync");
140
141        // Store it in an AsyncMutex
142        let cargo_toml_handle = Arc::new(AsyncMutex::new(cargo_toml));
143
144        // 3) Build an example CrateHandle with something to serialize
145        let handle = CrateHandleBuilder::default()
146            .crate_path(tmp_dir.path().to_path_buf())
147            .cargo_toml_handle(cargo_toml_handle)
148            .build()
149            .unwrap();
150
151        // 4) Serialize to JSON
152        let json_str = serde_json::to_string_pretty(&handle)
153            .expect("CrateHandle should serialize to JSON successfully");
154        trace!("Serialized JSON:\n{}", json_str);
155
156        // 5) Deserialize back into a new CrateHandle
157        let handle2: CrateHandle = serde_json::from_str(&json_str)
158            .expect("Deserialization of CrateHandle from JSON should succeed");
159
160        // 6) Confirm it round-trips by checking name/version
161        assert_eq!(handle2.name(), "some_crate");
162        let ver = handle2.version().expect("Should retrieve version from reloaded CargoToml");
163        assert_eq!(ver.to_string(), "0.1.2");
164    }
165}