scout-interpreter 0.5.2

A web crawling programming language
Documentation
use std::{
    env,
    path::{Path, PathBuf},
};

use scout_lexer::TokenKind;
use scout_parser::ast::{ExprKind, Identifier};

use crate::{EvalError, ImportError};

#[derive(Debug)]
pub struct ResolvedMod {
    pub ident: Identifier,
    pub filepath: String,
}

pub fn resolve_module(module: &ExprKind) -> Result<ResolvedMod, EvalError> {
    let ident = match module {
        ExprKind::Ident(ident) => Ok(ident.clone()),
        ExprKind::Infix(_, _, rhs) => match rhs.as_ref() {
            ExprKind::Ident(ident) => Ok(ident.clone()),
            _ => Err(EvalError::InvalidImport(ImportError::UnknownModule)),
        },
        _ => Err(EvalError::InvalidImport(ImportError::UnknownModule)),
    }?;
    let buf = resolve_module_file(module)?;
    let filepath = convert_path_buf(buf)?;
    Ok(ResolvedMod { filepath, ident })
}

fn resolve_std_file(ident: &Identifier) -> Result<PathBuf, EvalError> {
    if *ident == Identifier::new("std".into()) {
        let scout_dir = match env::var("SCOUT_PATH") {
            Ok(s) => Ok(Path::new(&s).to_path_buf()),
            Err(_) => match env::var("HOME") {
                Ok(s) => Ok(Path::new(&s).join("scout-lang")),
                Err(_) => Err(EvalError::OSError),
            },
        }?;
        let path = scout_dir.join("scout-lib").to_owned();
        Ok(path)
    } else {
        Ok(Path::new(&ident.name).to_owned())
    }
}

fn convert_path_buf(buf: PathBuf) -> Result<String, EvalError> {
    let res = buf
        .to_str()
        .ok_or(EvalError::InvalidImport(ImportError::PathError))?
        .to_owned();
    Ok(res)
}

fn resolve_module_file(module: &ExprKind) -> Result<PathBuf, EvalError> {
    match module {
        ExprKind::Ident(ident) => resolve_std_file(ident),
        ExprKind::Infix(lhs, TokenKind::DbColon, rhs) => match (lhs.as_ref(), rhs.as_ref()) {
            (ExprKind::Ident(base), ExprKind::Ident(file)) => {
                let buf = resolve_std_file(base)?.join(&file.name);
                Ok(buf)
            }
            (l @ ExprKind::Infix(_, TokenKind::DbColon, _), ExprKind::Ident(file)) => {
                let base = resolve_module_file(l)?;
                let buf = base.join(&file.name);
                Ok(buf)
            }
            _ => Err(EvalError::InvalidImport(ImportError::UnknownModule)),
        },
        _ => Err(EvalError::InvalidImport(ImportError::UnknownModule)),
    }
}