1use std::io::Read;
12use std::process::{Command, Stdio};
13use std::time::{Duration, Instant};
14
15use ready_set_sdk::manifest::Manifest;
16
17use crate::cache::{CacheKey, PluginCache};
18use crate::discovery::PluginEntry;
19
20const DESCRIBE_TIMEOUT: Duration = Duration::from_millis(100);
21const POLL_INTERVAL: Duration = Duration::from_millis(2);
22
23#[must_use]
27pub fn resolve_metadata(entry: &PluginEntry, cache: &mut PluginCache) -> Option<Manifest> {
28 if let Ok(key) = CacheKey::for_binary(&entry.binary_path) {
29 if let Some(cached) = cache.get(&key) {
30 return Some(cached.clone());
31 }
32
33 if let Some(m) = entry.manifest.clone() {
34 drop(cache.insert(&key, m.clone()));
35 return Some(m);
36 }
37
38 if let Some(m) = invoke_describe(entry) {
39 drop(cache.insert(&key, m.clone()));
40 return Some(m);
41 }
42 } else if let Some(m) = entry.manifest.clone() {
43 return Some(m);
44 }
45 None
46}
47
48fn invoke_describe(entry: &PluginEntry) -> Option<Manifest> {
49 let mut child = Command::new(&entry.binary_path)
50 .arg("__describe")
51 .stdin(Stdio::null())
52 .stdout(Stdio::piped())
53 .stderr(Stdio::null())
54 .spawn()
55 .ok()?;
56
57 let start = Instant::now();
58 loop {
59 match child.try_wait() {
60 Ok(Some(status)) => {
61 if !status.success() {
62 return None;
63 }
64 break;
65 },
66 Ok(None) => {},
67 Err(_) => return None,
68 }
69 if start.elapsed() >= DESCRIBE_TIMEOUT {
70 drop(child.kill());
71 drop(child.wait());
72 return None;
73 }
74 std::thread::sleep(POLL_INTERVAL);
75 }
76
77 let mut stdout = child.stdout.take()?;
78 let mut buf = String::new();
79 stdout.read_to_string(&mut buf).ok()?;
80 let line = buf.lines().next()?.trim();
81 let raw_value = serde_json::from_str::<serde_json::Value>(line).ok()?;
82 let manifest: Manifest = serde_json::from_value(raw_value).ok()?;
83 Some(manifest)
84}