use clap::{Parser, Subcommand, ValueEnum};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::path::{Path, PathBuf};
pub use hax_frontend_exporter_options::*;
#[derive(JsonSchema, Debug, Clone, Serialize, Deserialize)]
pub enum DebugEngineMode {
File(PathOrDash),
Interactive,
}
impl std::convert::From<&str> for DebugEngineMode {
fn from(s: &str) -> Self {
match s {
"i" | "interactively" => DebugEngineMode::Interactive,
s => DebugEngineMode::File(s.strip_prefix("file:").unwrap_or(s).into()),
}
}
}
#[derive(JsonSchema, Debug, Clone, Serialize, Deserialize)]
pub struct ForceCargoBuild {
pub data: u128,
}
impl std::default::Default for ForceCargoBuild {
fn default() -> Self {
ForceCargoBuild { data: 0 }
}
}
impl std::convert::From<&str> for ForceCargoBuild {
fn from(s: &str) -> Self {
use std::time::{SystemTime, UNIX_EPOCH};
if s == "false" {
ForceCargoBuild {
data: SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|r| r.as_millis())
.unwrap_or(0),
}
} else {
ForceCargoBuild::default()
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub enum PathOrDash {
Dash,
Path(PathBuf),
}
impl std::convert::From<&str> for PathOrDash {
fn from(s: &str) -> Self {
match s {
"-" => PathOrDash::Dash,
_ => PathOrDash::Path(PathBuf::from(s)),
}
}
}
impl PathOrDash {
pub fn open_or_stdout(&self) -> Box<dyn std::io::Write> {
use std::io::BufWriter;
match self {
PathOrDash::Dash => Box::new(BufWriter::new(std::io::stdout())),
PathOrDash::Path(path) => {
Box::new(BufWriter::new(std::fs::File::create(&path).unwrap()))
}
}
}
pub fn map_path<F: FnOnce(&Path) -> PathBuf>(&self, f: F) -> Self {
match self {
PathOrDash::Path(path) => PathOrDash::Path(f(path)),
PathOrDash::Dash => PathOrDash::Dash,
}
}
}
fn absolute_path(path: impl AsRef<std::path::Path>) -> std::io::Result<std::path::PathBuf> {
use path_clean::PathClean;
let path = path.as_ref();
let absolute_path = if path.is_absolute() {
path.to_path_buf()
} else {
std::env::current_dir()?.join(path)
}
.clean();
Ok(absolute_path)
}
pub trait NormalizePaths {
fn normalize_paths(&mut self);
}
impl NormalizePaths for PathBuf {
fn normalize_paths(&mut self) {
*self = absolute_path(&self).unwrap();
}
}
impl NormalizePaths for PathOrDash {
fn normalize_paths(&mut self) {
match self {
PathOrDash::Path(p) => p.normalize_paths(),
PathOrDash::Dash => (),
}
}
}
#[derive(JsonSchema, Subcommand, Debug, Clone, Serialize, Deserialize)]
pub enum Backend {
Fstar,
Coq,
Easycrypt,
}
impl fmt::Display for Backend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Backend::Fstar => write!(f, "fstar"),
Backend::Coq => write!(f, "coq"),
Backend::Easycrypt => write!(f, "easycrypt"),
}
}
}
#[derive(JsonSchema, Debug, Clone, Serialize, Deserialize)]
enum InclusionKind {
Included { with_deps: bool },
Excluded,
}
#[derive(JsonSchema, Debug, Clone, Serialize, Deserialize)]
struct InclusionClause {
kind: InclusionKind,
namespace: Namespace,
}
fn parse_inclusion_clause(
s: &str,
) -> Result<InclusionClause, Box<dyn std::error::Error + Send + Sync + 'static>> {
let s = s.trim();
if s.is_empty() {
Err("Expected `-` or `+`, got an empty string")?
}
let (prefix, mut namespace) = s.split_at(1);
let kind = match prefix {
"+" => InclusionKind::Included {
with_deps: match namespace.split_at(1) {
("!", rest) => {
namespace = rest;
false
}
_ => true,
},
},
"-" => InclusionKind::Excluded,
prefix => Err(format!("Expected `-` or `+`, got an `{prefix}`"))?,
};
Ok(InclusionClause {
kind,
namespace: namespace.to_string().into(),
})
}
#[derive(JsonSchema, Parser, Debug, Clone, Serialize, Deserialize)]
pub struct TranslationOptions {
#[arg(
value_parser = parse_inclusion_clause,
value_delimiter = ' ',
)]
#[arg(short, allow_hyphen_values(true))]
include_namespaces: Vec<InclusionClause>,
}
#[derive(JsonSchema, Parser, Debug, Clone, Serialize, Deserialize)]
pub struct BackendOptions {
#[command(subcommand)]
pub backend: Backend,
#[arg(long = "dry-run")]
pub dry_run: bool,
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
#[arg(short, long = "debug-engine")]
pub debug_engine: Option<DebugEngineMode>,
#[command(flatten)]
pub translation_options: TranslationOptions,
}
#[derive(JsonSchema, Subcommand, Debug, Clone, Serialize, Deserialize)]
pub enum ExporterCommand {
#[clap(name = "into")]
Backend(BackendOptions),
JSON {
#[arg(
short,
long = "output-file",
default_value = "hax_frontend_export.json"
)]
output_file: PathOrDash,
#[arg(
value_enum,
short,
long = "kind",
num_args = 0..=3,
default_values_t = [ExportBodyKind::Thir]
)]
kind: Vec<ExportBodyKind>,
#[arg(short = 'E', long = "include-extra", default_value = "false")]
include_extra: bool,
},
}
#[derive(
JsonSchema, ValueEnum, Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord,
)]
pub enum ExportBodyKind {
Thir,
MirBuilt,
MirConst,
}
#[derive(JsonSchema, Subcommand, Debug, Clone, Serialize, Deserialize)]
pub enum LinterCommand {
Hacspec,
Rust,
}
#[derive(JsonSchema, Subcommand, Debug, Clone, Serialize, Deserialize)]
pub enum Command {
#[command(flatten)]
ExporterCommand(ExporterCommand),
#[clap(subcommand, name = "lint", about = "Lint the code")]
LintCommand(LinterCommand),
}
#[derive(JsonSchema, Parser, Debug, Clone, Serialize, Deserialize)]
#[command(author, version = concat!("commit=", env!("HAX_GIT_COMMIT_HASH"), " ", "describe=", env!("HAX_GIT_DESCRIBE")), name = "hax", about, long_about = None)]
pub struct Options {
#[arg(
short = 'i',
long = "inline-macro-call",
value_name = "PATTERN",
value_parser,
value_delimiter = ',',
default_values = [
"hacspec_lib::array::array", "hacspec_lib::array::public_bytes", "hacspec_lib::array::bytes",
"hacspec_lib::math_integers::public_nat_mod", "hacspec_lib::math_integers::unsigned_public_integer",
],
)]
pub inline_macro_calls: Vec<Namespace>,
#[arg(default_values = Vec::<&str>::new(), short='C', allow_hyphen_values=true, num_args=1.., long="cargo-args", value_terminator=";")]
pub cargo_flags: Vec<String>,
#[command(subcommand)]
pub command: Option<Command>,
#[arg(long="enable-cargo-cache", action=clap::builder::ArgAction::SetTrue)]
pub force_cargo_build: ForceCargoBuild,
#[arg(long = "deps")]
pub deps: bool,
}
impl NormalizePaths for ExporterCommand {
fn normalize_paths(&mut self) {
use ExporterCommand::*;
match self {
JSON { output_file, .. } => output_file.normalize_paths(),
_ => (),
}
}
}
impl NormalizePaths for Command {
fn normalize_paths(&mut self) {
match self {
Command::ExporterCommand(cmd) => cmd.normalize_paths(),
_ => (),
}
}
}
impl NormalizePaths for Options {
fn normalize_paths(&mut self) {
if let Some(c) = &mut self.command {
c.normalize_paths()
}
}
}
impl From<Options> for hax_frontend_exporter_options::Options {
fn from(opts: Options) -> hax_frontend_exporter_options::Options {
hax_frontend_exporter_options::Options {
inline_macro_calls: opts.inline_macro_calls,
}
}
}
pub const ENV_VAR_OPTIONS_FRONTEND: &str = "DRIVER_HAX_FRONTEND_OPTS";