1#![deny(unsafe_code)]
7#![allow(missing_docs)]
8
9mod diff;
10mod differential;
11mod history;
12mod identity;
13mod link;
14mod merge;
15mod ownership;
16mod policy;
17mod status;
18mod sync;
19
20pub use diff::{ChangeKind, SemanticChange, SemanticDiff};
21pub use differential::{DifferentialCase, DifferentialResult, DifferentialRunner};
22pub use history::{HistoryEntry, SyncHistory};
23pub use identity::SemanticId;
24pub use link::{LinkedProject, SemanticMapping, LINK_DIR};
25pub use merge::{MergeDecision, SemanticConflict, SemanticMerge};
26pub use ownership::{ManualRegion, RegionKind, RegionOwnership};
27pub use policy::{LinkPolicy, SyncPolicy};
28pub use status::{DriftKind, LinkStatus, SemanticDrift};
29pub use sync::{sync_check, sync_link, SyncOptions, SyncReport};
30
31use link::LinkedProject as Link;
32use parallax_core::{ErrorCode, ParallaxError, Remediation};
33use parallax_project::{SourceLanguage, TargetLanguage};
34use std::path::{Path, PathBuf};
35
36pub async fn link_projects(
38 source: &Path,
39 target: &Path,
40 policy: SyncPolicy,
41) -> Result<LinkedProject, ParallaxError> {
42 Link::create(source, target, policy).await
43}
44
45pub fn load_link(path: &Path) -> Result<LinkedProject, ParallaxError> {
47 Link::load(path)
48}
49
50pub async fn link_status(path: &Path) -> Result<LinkStatus, ParallaxError> {
52 let link = load_link(path)?;
53 status::compute_status(&link).await
54}
55
56pub fn explain(path: &Path, location: &str) -> Result<ExplainReport, ParallaxError> {
58 let link = load_link(path)?;
59 let target_root = PathBuf::from(&link.target_root);
60 let entry = parallax_transmute::lookup_origin(&target_root, location).map_err(|e| {
61 e.remediate(Remediation::new(
62 "Ensure the target was produced by Transmute/Mirror and contains .plxmap.json",
63 ))
64 })?;
65 let node = link
66 .semantic_map
67 .iter()
68 .find(|m| {
69 m.target_file.as_deref() == Some(entry.generated_file.as_str())
70 || entry.semantic_node.contains(&m.qualified_name)
71 })
72 .cloned();
73 Ok(ExplainReport {
74 generated_file: entry.generated_file,
75 generated_line: entry.generated_line,
76 original_file: entry.original_file,
77 original_line: entry.original_line,
78 semantic_node: entry.semantic_node,
79 mapping: node,
80 confidence: "HIGH".into(),
81 })
82}
83
84pub fn why(path: &Path, target_file: &str) -> Result<WhyReport, ParallaxError> {
86 let link = load_link(path)?;
87 let hist = SyncHistory::load(&link.link_dir)?;
88 let last = hist.entries.last().cloned();
89 let related: Vec<_> = link
90 .semantic_map
91 .iter()
92 .filter(|m| {
93 m.target_file
94 .as_ref()
95 .map(|f| {
96 f.replace('\\', "/") == target_file.replace('\\', "/")
97 || target_file.ends_with(f.as_str())
98 })
99 .unwrap_or(false)
100 })
101 .cloned()
102 .collect();
103 Ok(WhyReport {
104 target_file: target_file.into(),
105 last_sync: last,
106 related_nodes: related,
107 })
108}
109
110pub fn rollback(path: &Path) -> Result<String, ParallaxError> {
112 history::rollback_last(path)
113}
114
115#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
117pub struct ExplainReport {
118 pub generated_file: String,
119 pub generated_line: u32,
120 pub original_file: String,
121 pub original_line: u32,
122 pub semantic_node: String,
123 pub mapping: Option<link::SemanticMapping>,
124 pub confidence: String,
125}
126
127#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
129pub struct WhyReport {
130 pub target_file: String,
131 pub last_sync: Option<HistoryEntry>,
132 pub related_nodes: Vec<link::SemanticMapping>,
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
137#[serde(rename_all = "snake_case")]
138pub enum PairTier {
139 Tier1,
140 Tier2,
141 Experimental,
142 Unsupported,
143}
144
145pub fn pair_tier(source: &SourceLanguage, target: &TargetLanguage) -> PairTier {
147 use parallax_connectors::PairMaturity;
148 match parallax_connectors::pair_maturity(source.as_str(), target.as_str()) {
149 PairMaturity::Tier1 => PairTier::Tier1,
150 PairMaturity::Tier2 => PairTier::Tier2,
151 PairMaturity::Experimental => PairTier::Experimental,
152 PairMaturity::Scaffold | PairMaturity::Unsupported => PairTier::Unsupported,
154 }
155}
156
157pub(crate) fn io_err(e: std::io::Error) -> ParallaxError {
158 ParallaxError::new(ErrorCode::Io, e.to_string()).with_source("parallax-mirror")
159}