cargo-port 0.1.4

A TUI for inspecting and managing Rust projects
use std::ops::Deref;
use std::ops::DerefMut;

use super::rust_info::RustInfo;
use crate::project::fields::ProjectFields;
use crate::project::git::CheckoutInfo;
use crate::project::git::WorktreeStatus;
use crate::project::info::ProjectInfo;
use crate::project::info::Visibility;
use crate::project::info::WorktreeHealth;
use crate::project::paths;
use crate::project::paths::AbsolutePath;
use crate::project::paths::DisplayPath;
use crate::project::paths::PackageName;
use crate::project::paths::RootDirectoryName;

/// A standalone Rust package project. Derefs to `RustInfo` for uniform access.
///
/// Construct via struct literal: `Package { path, name, rust: RustInfo { .. } }`.
/// All fields default to empty/none, so tests can use `..Default::default()`.
#[derive(Clone, Default)]
pub(crate) struct Package {
    pub path:            AbsolutePath,
    pub name:            Option<String>,
    pub worktree_status: WorktreeStatus,
    pub rust:            RustInfo,
}

impl Package {
    /// Cargo package name when present, otherwise directory leaf.
    pub fn package_name(&self) -> PackageName {
        PackageName(self.name.as_deref().map_or_else(
            || paths::directory_leaf(self.path.as_path()),
            str::to_string,
        ))
    }

    /// Language icon for the project list.
    pub const fn lang_icon() -> &'static str { "\u{1f980}" }
}

impl ProjectFields for Package {
    fn path(&self) -> &AbsolutePath { &self.path }

    fn name(&self) -> Option<&str> { self.name.as_deref() }

    fn visibility(&self) -> Visibility { self.rust.project_info.visibility }

    fn worktree_health(&self) -> WorktreeHealth { self.rust.project_info.worktree_health }

    fn disk_usage_bytes(&self) -> Option<u64> { self.rust.project_info.disk_usage_bytes }

    fn git_info(&self) -> Option<&CheckoutInfo> { self.rust.project_info.local_git_state.info() }

    fn info(&self) -> &ProjectInfo { &self.rust.project_info }

    fn display_path(&self) -> DisplayPath { self.path.display_path() }

    fn root_directory_name(&self) -> RootDirectoryName {
        RootDirectoryName(paths::directory_leaf(self.path.as_path()))
    }

    fn worktree_status(&self) -> &WorktreeStatus { &self.worktree_status }

    fn crates_io_name(&self) -> Option<&str> {
        self.name
            .as_deref()
            .filter(|_| self.rust.cargo.publishable())
    }
}

impl Deref for Package {
    type Target = RustInfo;

    fn deref(&self) -> &RustInfo { &self.rust }
}

impl DerefMut for Package {
    fn deref_mut(&mut self) -> &mut RustInfo { &mut self.rust }
}