diff --git a/Cargo.toml b/Cargo.toml index 6c3a9e6..dc3cf5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,4 +37,9 @@ num-bigint = "0.4" num-rational = "0.4" num-traits = "0.2" algonaut_core = "0.4" -hex = "0.4" \ No newline at end of file +hex = "0.4" +regex = "1.10" +clap = { version ="4.5", features = ["derive"]} +ariadne = { version = "0.4", features = ["auto-color"] } +anyhow = "1.0" +walkdir = "2.5" \ No newline at end of file diff --git a/crates/folidity/Cargo.toml b/crates/folidity/Cargo.toml index 68b9d7b..e7cba82 100644 --- a/crates/folidity/Cargo.toml +++ b/crates/folidity/Cargo.toml @@ -12,4 +12,9 @@ version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -folidity-parser = { workspace = true } \ No newline at end of file +folidity-parser = { workspace = true } +folidity-semantics = { workspace = true } +clap = { workspace = true } +ariadne = { workspace = true } +anyhow = { workspace = true } +walkdir = { workspace = true } \ No newline at end of file diff --git a/crates/folidity/src/cmd/mod.rs b/crates/folidity/src/cmd/mod.rs new file mode 100644 index 0000000..e7d8fa5 --- /dev/null +++ b/crates/folidity/src/cmd/mod.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use clap::Subcommand; + +use self::new::NewCommand; + +mod new; + +#[derive(Subcommand)] +pub enum Commands { + New(NewCommand), +} + +impl Commands { + pub fn run(&self) -> Result<()> { + match self { + Commands::New(cmd) => cmd.run(), + } + } +} diff --git a/crates/folidity/src/cmd/new.rs b/crates/folidity/src/cmd/new.rs new file mode 100644 index 0000000..7f1a032 --- /dev/null +++ b/crates/folidity/src/cmd/new.rs @@ -0,0 +1,61 @@ +use anyhow::Result; +use std::{ + ffi::OsString, + fs::{ + self, + create_dir, + File, + }, + io::Write, + path::Path, +}; +use walkdir::WalkDir; + +use clap::Args; + +/// Creates a new templated `folidity` counter project. +/// with a basic contract, README and approval teal code. +#[derive(Args)] +pub struct NewCommand { + /// Path to the new project. + /// If empty, the project will be created in the current dir. + #[clap(value_parser)] + name: Option, +} + +impl NewCommand { + pub fn run(&self) -> Result<()> { + let out_dir = self.name.clone().unwrap_or(OsString::from(".")); + let out_path = Path::new(&out_dir); + if out_path.exists() { + for entry in WalkDir::new(out_path) + .follow_links(true) + .into_iter() + .filter_map(|e| e.ok()) + { + let f_name = entry.file_name().to_string_lossy(); + let sec = entry.metadata()?.modified()?; + + if f_name.ends_with(".fol") && sec.elapsed()?.as_secs() < 86400 { + anyhow::bail!( + "Project with this name already exist in {}", + out_dir.to_str().unwrap() + ); + } + } + } else { + create_dir(&out_dir).map(|_| anyhow::anyhow!("Cannot create project directory."))?; + } + + let contract_content = include_str!("../../../../examples/counter/counter.fol"); + let readme_content = include_str!("../../../../examples/counter/README.md"); + + let mut contract_file = File::create(Path::new(&out_dir).join("counter.fol"))?; + contract_file.write_all(contract_content.to_string().as_bytes())?; + + let mut readme_file = File::create(Path::new(&out_dir).join("README.md"))?; + readme_file.write_all(readme_content.to_string().as_bytes())?; + + Ok(()) + } +} diff --git a/crates/folidity/src/main.rs b/crates/folidity/src/main.rs index e7a11a9..a0c171e 100644 --- a/crates/folidity/src/main.rs +++ b/crates/folidity/src/main.rs @@ -1,3 +1,22 @@ +use clap::Parser; +use cmd::Commands; + +mod cmd; + +#[derive(Parser)] +#[command(author = env!("CARGO_PKG_AUTHORS"), version = concat!("version ", env!("CARGO_PKG_VERSION")), about = env!("CARGO_PKG_DESCRIPTION"), subcommand_required = true)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + fn main() { - println!("Hello, world!"); + let cli = Cli::parse(); + match cli.command.run() { + Ok(()) => {} + Err(err) => { + eprintln!("{err:?}"); + std::process::exit(1); + } + } } diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 095629e..ae3bf16 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -16,7 +16,7 @@ lalrpop-util = { workspace = true } thiserror = { workspace = true } derive-node = { workspace = true } folidity-diagnostics = { workspace = true } -regex = "*" +regex = { workspace = true } [build-dependencies] # <-- We added this and everything after! lalrpop = { workspace = true } \ No newline at end of file diff --git a/examples/counter/README.md b/examples/counter/README.md new file mode 100644 index 0000000..786bf1a --- /dev/null +++ b/examples/counter/README.md @@ -0,0 +1,3 @@ +# Verifiable counter + +This is a simple counter smart contract. \ No newline at end of file diff --git a/examples/counter/counter.fol b/examples/counter/counter.fol new file mode 100644 index 0000000..ae2efed --- /dev/null +++ b/examples/counter/counter.fol @@ -0,0 +1,40 @@ +state CounterState { + counter: int, +} st [ + # example bounds + counter < 1000, + counter > -1000 +] + +# This is an constructor. +@init +# Anyone can call this function. +@(any) +fn () initialise() when () -> CounterState { + move CounterState : { 0 }; +} + +@(any) +fn () incr_by(value: int) when CounterState s -> CounterState +st [ + value > 100, + value < 100 +] { + let value = s.counter + value; + move CounterState : { value }; +} + +@(any) +fn () decr_by(value: int) when CounterState s -> CounterState +st [ + value > 100, + value < 100 +] { + let value = s.counter - value; + move CounterState : { value }; +} + +@(any) +view(CounterState s) fn int get_value() { + return s.counter; +} \ No newline at end of file diff --git a/examples/flipper/README.md b/examples/flipper/README.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/flipper/flipper.fol b/examples/flipper/flipper.fol new file mode 100644 index 0000000..e69de29