tessera-mobile 0.0.0

Rust on mobile made easy.
Documentation
use std::{
    fs, io,
    path::{Path, PathBuf},
};

use serde::Deserialize;
use thiserror::Error;

use crate::util::{
    self, Git,
    submodule::{self, Submodule},
};

use super::{LookupError, Pack};

#[derive(Debug, Error)]
pub enum FancyPackParseError {
    #[error("Failed to read remote template pack spec {path}: {cause}")]
    ReadFailed { path: PathBuf, cause: io::Error },
    #[error("Failed to parse remote template pack spec {path}: {cause}")]
    ParseFailed {
        path: PathBuf,
        cause: toml::de::Error,
    },
    #[error(transparent)]
    NoHomeDir(util::NoHomeDir),
    #[error("Failed to lookup base template pack: {0}")]
    BaseFailed(Box<LookupError>),
}

#[derive(Debug, Error)]
pub enum FancyPackResolveError {
    #[error("Failed to initialize submodule: {0}")]
    SubmoduleFailed(submodule::Error),
    #[error("Template pack wasn't found at {0}")]
    PackNotFound(PathBuf),
}

#[derive(Clone, Debug)]
pub struct FancyPack {
    path: PathBuf,
    base: Option<Box<Pack>>,
    submodule: Option<Submodule>,
}

impl FancyPack {
    pub fn parse(path: impl AsRef<Path>) -> Result<Self, FancyPackParseError> {
        #[derive(Deserialize)]
        struct Raw {
            path: PathBuf,
            base: Option<String>,
            submodule: Option<Submodule>,
        }

        let path = path.as_ref();
        let raw = {
            let toml_str =
                fs::read_to_string(path).map_err(|cause| FancyPackParseError::ReadFailed {
                    path: path.to_owned(),
                    cause,
                })?;
            toml::from_str::<Raw>(&toml_str).map_err(|cause| FancyPackParseError::ParseFailed {
                path: path.to_owned(),
                cause,
            })?
        };

        let raw_path = path
            .parent()
            .map(|p| p.join(&raw.path))
            .unwrap_or(raw.path.clone());

        let real_path = util::expand_home(raw_path).map_err(FancyPackParseError::NoHomeDir)?;
        let this = Self {
            path: real_path,
            base: raw
                .base
                .map(|name| {
                    Pack::lookup(
                        path.parent()
                            .expect("developer error: templates dir had no parent"),
                        name,
                    )
                })
                .transpose()
                .map_err(Box::new)
                .map_err(FancyPackParseError::BaseFailed)?
                .map(Box::new),
            submodule: raw.submodule,
        };
        log::info!("template pack {:#?}", this);
        Ok(this)
    }

    pub fn submodule_path(&self) -> Option<&Path> {
        self.submodule.as_ref().map(|submodule| submodule.path())
    }

    pub fn resolve(
        &self,
        git: Git<'_>,
        submodule_commit: Option<&str>,
    ) -> Result<Vec<&Path>, FancyPackResolveError> {
        if let Some(submodule) = &self.submodule {
            submodule
                .init(git, submodule_commit)
                .map_err(FancyPackResolveError::SubmoduleFailed)?;
        }
        if self.path.exists() {
            let mut paths = self
                .base
                .as_ref()
                .map(|base| {
                    base.resolve(
                        git,
                        submodule_commit.filter(|_| base.submodule_path() == self.submodule_path()),
                    )
                })
                .transpose()?
                .unwrap_or_default();
            paths.push(&self.path);
            Ok(paths)
        } else {
            Err(FancyPackResolveError::PackNotFound(self.path.clone()))
        }
    }
}