shank_gov_cli/
lib.rs

1use std::{
2    fs::{self, File},
3    io::Write,
4    path::{Path, PathBuf},
5};
6
7use anyhow::{anyhow, format_err, Result};
8use clap::Parser;
9use log::{debug, info};
10use shank_gov_idl::{extract_idl, manifest::Manifest, ParseIdlOpts};
11
12pub const VERSION: &str = env!("CARGO_PKG_VERSION");
13
14#[derive(Debug, Parser)]
15#[clap(version = VERSION)]
16pub struct Opts {
17    #[clap(subcommand)]
18    pub command: Command,
19}
20
21#[derive(Debug, Parser)]
22pub enum Command {
23    Idl {
24        /// Output directory for the IDL JSON.
25        #[clap(short, long, default_value = "idl")]
26        out_dir: String,
27
28        /// Directory of program crate for which to generate the IDL.
29        #[clap(short = 'r', long)]
30        crate_root: Option<String>,
31
32        /// Manually specify and override the address in the IDL
33        #[clap(short = 'p', long)]
34        program_id: Option<String>,
35    },
36}
37
38pub fn entry(opts: Opts) -> Result<()> {
39    match opts.command {
40        Command::Idl {
41            out_dir,
42            crate_root,
43            program_id,
44        } => idl(out_dir, crate_root, program_id),
45    }
46}
47
48pub fn try_resolve_path(p: Option<String>, label: &str) -> Result<PathBuf> {
49    let p = match p {
50        Some(crate_root) => Ok(Path::new(&crate_root).to_path_buf()),
51        None => {
52            debug!("No {} provided, assuming in current dir", label);
53            std::env::current_dir()
54        }
55    }?;
56
57    let p = if p.is_absolute() {
58        Ok(p)
59    } else {
60        debug!("{} is relative, resolving from current dir", label);
61        std::env::current_dir().map(|x| x.join(p))
62    }?;
63
64    Ok(p)
65}
66
67pub fn idl(
68    out_dir: String,
69    crate_root: Option<String>,
70    program_id: Option<String>,
71) -> Result<()> {
72    // Resolve input and output directories
73    let crate_root = try_resolve_path(crate_root, "crate_root")?;
74    let out_dir = try_resolve_path(Some(out_dir), "out_dir")?;
75    fs::create_dir_all(&out_dir).map_err(|err| {
76        format_err!(
77            "Unable to create out_dir ({}), {}",
78            &out_dir.display(),
79            err
80        )
81    })?;
82
83    // Resolve info about lib for which we generate IDL
84    let cargo_toml = crate_root.join("Cargo.toml");
85    if !cargo_toml.exists() {
86        return Err(anyhow!(
87            "Did not find Cargo.toml at the path: {}",
88            crate_root.display()
89        ));
90    }
91    let manifest = Manifest::from_path(&cargo_toml)?;
92    let lib_rel_path = manifest
93        .lib_rel_path()
94        .ok_or(anyhow!("Program needs to be a lib"))?;
95
96    let lib_full_path_str = crate_root.join(lib_rel_path);
97    let lib_full_path =
98        lib_full_path_str.to_str().ok_or(anyhow!("Invalid Path"))?;
99
100    // Extract IDL and convert to JSON
101    let opts = ParseIdlOpts {
102        program_address_override: program_id,
103        ..ParseIdlOpts::default()
104    };
105    let idl = extract_idl(lib_full_path, opts)?
106        .ok_or(anyhow!("No IDL could be extracted"))?;
107    let idl_json = idl.try_into_json()?;
108    let y = idl_json.split("],\n  \"errors\": [").collect::<Vec<&str>>();
109    let mut final_json = y[0].to_string();
110
111    final_json.push_str("
112    ,
113    {
114        \"name\": \"UnixTimestamp\",
115        \"type\": {
116            \"kind\": \"alias\",
117            \"value\": \"i64\"
118        }
119    },
120    {
121        \"name\": \"Slot\",
122        \"type\": {
123            \"kind\": \"alias\",
124            \"value\": \"u64\"
125        }
126    }
127    ],
128    \"errors\": [");
129
130    final_json.push_str(y[1]);
131
132    // Write to JSON file
133    let name = manifest.lib_name()?;
134    let idl_json_path = out_dir.join(format!("{}.json", name));
135    let mut idl_json_file = File::create(&idl_json_path)?;
136    info!("Writing IDL to {}", &idl_json_path.display());
137
138    idl_json_file.write_all(final_json.as_bytes())?;
139
140    Ok(())
141}