ordinary 0.11.1

Ordinary CLI
Documentation
// Copyright (C) 2026 The Ordinary Authors.
//
// SPDX-License-Identifier: BSD-3-Clause

use crate::HostCallFlags;
use crate::cmds::accounts::get_current_account;
use anyhow::bail;
use clap::Subcommand;
use ordinary_config::{FunctionFfiVersion, FunctionToolchain, OrdinaryConfig};
use std::path::Path;
use tracing::instrument;

#[derive(Subcommand, Debug)]
pub enum Functions {
    /// add a new function to your Ordinary project
    #[clap(visible_aliases(["create", "new"]))]
    Add {
        /// function name
        name: String,
        /// language the function runs
        #[arg(short, long, default_value = "rust")]
        toolchain: FunctionToolchain,
    },
    /// build just the functions
    ///
    /// Note: will load environment variables from `.env`
    Build {
        /// name of a specific function to build (optional).
        /// will install all when the `--name` flag is not passed.
        #[arg(short, long)]
        name: Option<String>,
        /// build functions without checking the cache
        #[arg(short, long, default_value_t = false)]
        force: bool,
    },
    /// upload functions to application running on `ordinaryd` instance
    Upload {
        #[command(flatten)]
        host: HostCallFlags,

        /// name of a specific function to upload (optional).
        /// will upload all when the `--name` flag is not passed.
        #[arg(short, long)]
        name: Option<String>,
    },
}

impl Functions {
    #[instrument(skip_all, name = "functions")]
    pub async fn handle(&self, project: &str) -> anyhow::Result<()> {
        match self {
            Self::Add { name, toolchain } => {
                ordinary_function::modify::add(Path::new(project), name, toolchain)?;
            }
            Self::Build { name, force } => {
                let env_file = Path::new(project).join(".env");
                if env_file.exists() {
                    dotenvy::from_path(env_file)?;
                }

                let config = OrdinaryConfig::get("../../../../../..", true)?;
                config.validate()?;

                if let Some(name) = name {
                    if let Some(function_config) = config.functions.and_then(|functions| {
                        functions.iter().find_map(|a| {
                            if a.name_validated() == name {
                                Some(a.clone())
                            } else {
                                None
                            }
                        })
                    }) {
                        match function_config.ffi_validated().version {
                            FunctionFfiVersion::V1 => {
                                ordinary_function::build::v1::build(
                                    &function_config,
                                    Path::new(project),
                                    *force,
                                )?;
                            }
                        }
                    } else {
                        bail!("function with name '{name}' doesn't exist");
                    }
                } else if let Some(function_configs) = &config.functions {
                    for function_config in function_configs {
                        match function_config.ffi_validated().version {
                            FunctionFfiVersion::V1 => {
                                ordinary_function::build::v1::build(
                                    function_config,
                                    Path::new(project),
                                    *force,
                                )?;
                            }
                        }
                    }
                }
            }
            Self::Upload { name, host } => {
                let account = get_current_account(host.insecure)?;
                let client = host.client(&account)?;

                if let Some(name) = name {
                    client.functions_upload(project, name).await?;
                } else {
                    client.functions_upload_all(project).await?;
                }
            }
        }

        Ok(())
    }
}