use crate::client::bridge::VirtuosoClient;
use crate::commands::schematic::parse_skill_json;
use crate::error::{Result, VirtuosoError};
use serde_json::{json, Value};
pub fn open(lib: &str, cell: &str, view: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.open_session(lib, cell, view);
let r = client.execute_skill(&skill, None)?;
if !r.skill_ok() {
return Err(VirtuosoError::Execution(format!(
"Failed to open maestro session: {}",
r.output
)));
}
Ok(json!({
"status": "success",
"session": r.output.trim_matches('"'),
"lib": lib, "cell": cell, "view": view,
}))
}
pub fn close(session: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.close_session(session);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"status": if r.skill_ok() { "success" } else { "error" },
"session": session,
"output": r.output,
}))
}
pub fn list_sessions() -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.list_sessions();
let r = client.execute_skill(&skill, None)?;
if !r.skill_ok() {
return Err(VirtuosoError::Execution(format!(
"Failed to list sessions: {}",
r.output
)));
}
parse_skill_json(&r.output)
}
pub fn set_var(session: &str, name: &str, value: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.set_var(session, name, value);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"status": if r.skill_ok() { "success" } else { "error" },
"session": session, "variable": name, "value": value,
"output": r.output,
}))
}
pub fn get_analyses(session: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.get_analyses(session);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"session": session,
"analyses": r.output.trim_matches('"'),
}))
}
pub fn set_analysis(session: &str, analysis_type: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.set_analysis(session, analysis_type);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"status": if r.skill_ok() { "success" } else { "error" },
"session": session,
"analysis": analysis_type,
}))
}
pub fn run(session: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.run_simulation(session);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"status": if r.skill_ok() { "launched" } else { "error" },
"session": session,
"output": r.output.trim_matches('"'),
}))
}
pub fn add_output(session: &str, name: &str, expr: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.add_output(session, name, expr);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"status": if r.skill_ok() { "success" } else { "error" },
"session": session, "name": name, "expression": expr,
"output": r.output,
}))
}
pub fn save(session: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.save_setup(session);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"status": if r.skill_ok() { "success" } else { "error" },
"session": session,
}))
}
pub fn export(session: &str, path: &str) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.export_results(session, path);
let r = client.execute_skill(&skill, None)?;
Ok(json!({
"status": if r.skill_ok() { "success" } else { "error" },
"session": session,
"path": path,
"output": r.output,
}))
}
pub fn session_info(session: Option<&str>) -> Result<Value> {
let client = VirtuosoClient::from_env()?;
let skill = client.maestro.focused_window_skill();
let r = client.execute_skill(&skill, None)?;
let focused = extract_first_skill_string(&r.output);
let parsed = focused.as_deref().and_then(parse_ade_title);
let run_dir = if let Some(s) = session {
let skill2 = client.maestro.run_dir_skill(s);
let r2 = client.execute_skill(&skill2, None)?;
if r2.skill_ok() {
Some(r2.output.trim_matches('"').to_string())
} else {
None
}
} else {
None
};
Ok(json!({
"status": "success",
"focused_window": focused,
"session": session,
"application": parsed.as_ref().map(|p| p.application.as_str()),
"lib": parsed.as_ref().map(|p| p.lib.as_str()),
"cell": parsed.as_ref().map(|p| p.cell.as_str()),
"view": parsed.as_ref().map(|p| p.view.as_str()),
"editable": parsed.as_ref().map(|p| p.editable),
"unsaved_changes": parsed.as_ref().map(|p| p.unsaved_changes),
"run_dir": run_dir,
}))
}
fn extract_first_skill_string(s: &str) -> Option<String> {
let s = s.trim().strip_prefix('(')?;
let start = s.find('"')?;
let inner = &s[start + 1..];
let end = inner.find('"')?;
Some(inner[..end].to_string())
}
struct AdeWindowInfo {
application: String,
lib: String,
cell: String,
view: String,
editable: bool,
unsaved_changes: bool,
}
fn parse_ade_title(title: &str) -> Option<AdeWindowInfo> {
let rest = title.strip_prefix("ADE ")?;
let (app, rest) = if let Some(r) = rest.strip_prefix("Assembler ") {
("assembler", r)
} else if let Some(r) = rest.strip_prefix("Explorer ") {
("explorer", r)
} else {
return None;
};
let (editable, rest) = if let Some(r) = rest.strip_prefix("Editing: ") {
(true, r)
} else if let Some(r) = rest.strip_prefix("Reading: ") {
(false, r)
} else {
return None;
};
let mut parts = rest.split_whitespace();
let lib = parts.next()?.to_string();
let cell = parts.next()?.to_string();
let view_raw = parts.next()?;
let unsaved_changes = view_raw.ends_with('*');
let view = view_raw.trim_end_matches('*').to_string();
Some(AdeWindowInfo {
application: app.to_string(),
lib,
cell,
view,
editable,
unsaved_changes,
})
}