corsa 0.3.2

Production-oriented Rust bindings, orchestration layers, and Node integration for typescript-go
Documentation
#![allow(dead_code)]

use std::{path::PathBuf, sync::Arc};

use corsa::{
    api::{
        ApiFileSystem, ApiMode, ApiProfile, ApiSpawnConfig, DirectoryEntries,
        FileSystemCapabilities, ReadFileResult,
    },
    lsp::LspSpawnConfig,
};
use corsa_core::fast::{CompactString, FastMap, SmallVec};

pub fn mock_binary() -> PathBuf {
    PathBuf::from(env!("CARGO_BIN_EXE_mock_tsgo"))
}

pub fn workspace_root() -> PathBuf {
    let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    loop {
        if dir.join("pnpm-workspace.yaml").exists() {
            return dir;
        }
        assert!(
            dir.pop(),
            "failed to locate workspace root from {}",
            env!("CARGO_MANIFEST_DIR")
        );
    }
}

pub fn test_cwd() -> PathBuf {
    let cwd = std::env::temp_dir().join("corsa-tests");
    std::fs::create_dir_all(&cwd).unwrap();
    cwd
}

pub fn api_config(mode: ApiMode) -> ApiSpawnConfig {
    ApiSpawnConfig::new(mock_binary())
        .with_mode(mode)
        .with_cwd(test_cwd())
}

pub fn api_profile(id: &str, mode: ApiMode) -> ApiProfile {
    ApiProfile::new(id, api_config(mode))
}

pub fn lsp_config() -> LspSpawnConfig {
    LspSpawnConfig::new(mock_binary()).with_cwd(test_cwd())
}

pub fn resolved_real_tsgo_binary() -> Option<PathBuf> {
    if let Some(path) = std::env::var_os("TSGO_EXECUTABLE") {
        let path = PathBuf::from(path);
        if path.exists() {
            return Some(path);
        }
    }
    [
        workspace_root().join(".cache/tsgo"),
        workspace_root().join(".cache/tsgo.exe"),
        workspace_root().join("ref/typescript-go/.cache/tsgo"),
        workspace_root().join("ref/typescript-go/.cache/tsgo.exe"),
        workspace_root().join("ref/typescript-go/built/local/tsgo"),
        workspace_root().join("ref/typescript-go/built/local/tsgo.exe"),
    ]
    .into_iter()
    .find(|path| path.exists())
}

pub fn real_tsgo_binary() -> PathBuf {
    resolved_real_tsgo_binary().unwrap_or_else(|| {
        workspace_root().join(if cfg!(windows) {
            ".cache/tsgo.exe"
        } else {
            ".cache/tsgo"
        })
    })
}

pub fn real_dataset() -> PathBuf {
    workspace_root().join("ref/typescript-go/_packages/api/tsconfig.json")
}

pub fn real_api_config(mode: ApiMode) -> Option<ApiSpawnConfig> {
    let binary = resolved_real_tsgo_binary()?;
    let dataset = real_dataset();
    if !dataset.exists() {
        return None;
    }
    Some(
        ApiSpawnConfig::new(binary)
            .with_mode(mode)
            .with_cwd(workspace_root()),
    )
}

pub fn virtual_fs(entries: &[(&str, &str)]) -> Arc<VirtualFs> {
    let files = entries
        .iter()
        .map(|(path, content)| (CompactString::from(*path), CompactString::from(*content)))
        .collect();
    Arc::new(VirtualFs { files })
}

pub struct VirtualFs {
    files: FastMap<CompactString, CompactString>,
}

impl ApiFileSystem for VirtualFs {
    fn capabilities(&self) -> FileSystemCapabilities {
        FileSystemCapabilities {
            read_file: true,
            file_exists: true,
            directory_exists: true,
            get_accessible_entries: true,
            realpath: true,
        }
    }

    fn read_file(&self, path: &str) -> ReadFileResult {
        self.files
            .get(path)
            .cloned()
            .map(ReadFileResult::Content)
            .unwrap_or(ReadFileResult::Fallback)
    }

    fn file_exists(&self, path: &str) -> Option<bool> {
        Some(self.files.contains_key(path))
    }

    fn directory_exists(&self, path: &str) -> Option<bool> {
        Some(path == "/virtual")
    }

    fn get_accessible_entries(&self, path: &str) -> Option<DirectoryEntries> {
        (path == "/virtual").then(|| DirectoryEntries {
            files: self
                .files
                .keys()
                .filter_map(|path| path.strip_prefix("/virtual/").map(CompactString::from))
                .collect(),
            directories: SmallVec::new(),
        })
    }

    fn realpath(&self, path: &str) -> Option<CompactString> {
        Some(path.into())
    }
}