summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2021-04-06 17:06:11 +0200
committerMatthias Beyer <mail@beyermatthias.de>2021-04-06 17:06:11 +0200
commitfa655bbe8acae9c7f5d46e977ebea01ac0572327 (patch)
treeba1617c161214df4e589f931cf205ef383a3e52c
parenta70d760dd4d24d2d2963f6e48e3fec027f1e8305 (diff)
Implement first CLI
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--Cargo.toml26
-rw-r--r--src/backend/backend.rs7
-rw-r--r--src/backend/datetime.rs7
-rw-r--r--src/backend/mime.rs6
-rw-r--r--src/backend/node.rs1
-rw-r--r--src/backend/payload.rs28
-rw-r--r--src/cli.rs46
-rw-r--r--src/consts.rs4
-rw-r--r--src/main.rs70
9 files changed, 184 insertions, 11 deletions
diff --git a/Cargo.toml b/Cargo.toml
index f24ae6f..d557fb9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,15 +17,21 @@ edition = "2018"
[dependencies]
# TODO: Replace with thiserror
-anyhow = "1"
+anyhow = "1"
-async-trait = "0.1"
-cid = "0.6"
-chrono = "0.4"
-daglib = { git = "https://git.sr.ht/~matthiasbeyer/daglib", branch = "master" }
-ipfs-embed = "0.19"
-libipld = "0.11"
-libipld-cbor = "0.11"
-mime = "0.3"
-tokio = { version = "1", features = ["full"] }
+async-trait = "0.1"
+cid = "0.6"
+chrono = "0.4"
+clap-v3 = "3.0.0-beta.1"
+daglib = { git = "https://git.sr.ht/~matthiasbeyer/daglib", branch = "master" }
+ipfs-embed = "0.19"
+libipld = "0.11"
+libipld-cbor = "0.11"
+libp2p = "0.36"
+libp2p-bitswap = "*"
+libp2p-core = "*"
+libp2p-gossipsub = "*"
+libp2p-ping = "*"
+mime = "0.3"
+tokio = { version = "1", features = ["full"] }
diff --git a/src/backend/backend.rs b/src/backend/backend.rs
index 3e00f0f..33a8906 100644
--- a/src/backend/backend.rs
+++ b/src/backend/backend.rs
@@ -43,6 +43,13 @@ impl IpfsEmbedBackend {
ipfs_embed::Ipfs::new(config).await.map(Arc::new).map(|ipfs| IpfsEmbedBackend { ipfs })
}
+ pub async fn new_with_config(cfg: ipfs_embed::Config) -> Result<Self> {
+ ipfs_embed::Ipfs::new(cfg)
+ .await
+ .map(Arc::new)
+ .map(|ipfs| IpfsEmbedBackend { ipfs })
+ }
+
pub async fn write_payload(&self, payload: &crate::backend::Payload) -> Result<cid::Cid> {
let block = libipld::block::Block::encode(libipld::cbor::DagCborCodec, libipld::multihash::Code::Blake3_256, &payload)?;
self.ipfs
diff --git a/src/backend/datetime.rs b/src/backend/datetime.rs
index 650a159..ec99282 100644
--- a/src/backend/datetime.rs
+++ b/src/backend/datetime.rs
@@ -3,6 +3,13 @@ use anyhow::Error;
#[derive(Debug, Eq, PartialEq)]
pub struct DateTime(chrono::DateTime<chrono::Utc>);
+impl From<chrono::DateTime<chrono::Utc>> for DateTime {
+ fn from(dt: chrono::DateTime<chrono::Utc>) -> Self {
+ DateTime(dt)
+ }
+}
+
+
impl libipld::codec::Encode<libipld_cbor::DagCborCodec> for DateTime {
fn encode<W: std::io::Write>(&self, c: libipld_cbor::DagCborCodec, w: &mut W) -> libipld::error::Result<()> {
self.0.to_rfc3339().encode(c, w).map_err(Error::from)
diff --git a/src/backend/mime.rs b/src/backend/mime.rs
index d65761e..9ea1cb8 100644
--- a/src/backend/mime.rs
+++ b/src/backend/mime.rs
@@ -3,6 +3,12 @@ use anyhow::Error;
#[derive(Debug, Eq, PartialEq)]
pub struct MimeType(mime::Mime);
+impl From<mime::Mime> for MimeType {
+ fn from(mime: mime::Mime) -> Self {
+ MimeType(mime)
+ }
+}
+
impl<C: libipld::codec::Codec> libipld::codec::Encode<C> for MimeType {
fn encode<W: std::io::Write>(&self, _c: C, w: &mut W) -> libipld::error::Result<()> {
w.write_all(self.0.essence_str().as_bytes()).map_err(Error::from)
diff --git a/src/backend/node.rs b/src/backend/node.rs
index 800d503..a068405 100644
--- a/src/backend/node.rs
+++ b/src/backend/node.rs
@@ -33,4 +33,5 @@ impl Node {
payload
}
}
+
}
diff --git a/src/backend/payload.rs b/src/backend/payload.rs
index fcddb50..856fcda 100644
--- a/src/backend/payload.rs
+++ b/src/backend/payload.rs
@@ -8,3 +8,31 @@ pub struct Payload {
content: Vec<u8>,
}
+impl Payload {
+ pub fn new(mime: MimeType, timestamp: DateTime) -> Self {
+ Payload { mime, timestamp, content: Vec::new() }
+ }
+
+ pub fn now_from_text(text: String) -> Payload {
+ let mime = MimeType::from(mime::TEXT_PLAIN_UTF_8);
+ let timestamp = DateTime::from(chrono::offset::Utc::now());
+
+ Self::new(mime, timestamp).with_content(text.as_bytes().to_vec())
+ }
+
+ pub fn with_content(mut self, v: Vec<u8>) -> Self {
+ self.content = v;
+ self
+ }
+
+ pub fn with_mimetype(mut self, mime: MimeType) -> Self {
+ self.mime = mime;
+ self
+ }
+
+ pub fn with_timestamp(mut self, ts: DateTime) -> Self {
+ self.timestamp = ts;
+ self
+ }
+}
+
diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..4df70fa
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,46 @@
+use clap::crate_authors;
+use clap::crate_version;
+use clap::App;
+use clap::Arg;
+
+pub fn app<'a>() -> App<'a> {
+ App::new("distrox")
+ .author(crate_authors!())
+ .version(crate_version!())
+ .about("Distributed social network")
+
+ .subcommand(App::new("create-profile")
+ .author(crate_authors!())
+ .version(crate_version!())
+ .about("Create a new profile")
+
+ .arg(Arg::with_name("content")
+ .index(1)
+ .multiple(false)
+ .takes_value(true)
+ .value_name("CONTENT")
+ .help("The text posting as first profile content")
+ )
+ )
+
+ .subcommand(App::new("post")
+ .author(crate_authors!())
+ .version(crate_version!())
+ .about("Post to a profile")
+ .arg(Arg::with_name("head")
+ .index(1)
+ .multiple(false)
+ .takes_value(true)
+ .value_name("HEAD")
+ .help("Post with this HEAD as parent")
+ )
+
+ .arg(Arg::with_name("content")
+ .index(2)
+ .multiple(false)
+ .takes_value(true)
+ .value_name("TEXT")
+ .help("Post this TEXT as text/text")
+ )
+ )
+}
diff --git a/src/consts.rs b/src/consts.rs
new file mode 100644
index 0000000..e5c880c
--- /dev/null
+++ b/src/consts.rs
@@ -0,0 +1,4 @@
+pub fn v1() -> String {
+ String::from("1")
+}
+
diff --git a/src/main.rs b/src/main.rs
index ec907e8..ca89d15 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,9 +1,77 @@
+use std::path::PathBuf;
+
use anyhow::Result;
+use daglib::DagBackend;
+
+extern crate clap_v3 as clap;
mod backend;
+mod cli;
+mod consts;
mod profile;
#[tokio::main]
async fn main() -> Result<()> {
- Ok(())
+ let app = crate::cli::app();
+
+ let mut backend = {
+ // Testing configuration for the IPFS node in the backend.
+
+ let tmp = PathBuf::from("/tmp/distrox.tmp");
+ let sconf = ipfs_embed::StorageConfig {
+ path: Some(tmp),
+ cache_size_blocks: 100_000, // blocks kepts before GC
+ cache_size_bytes: 1024 * 1024 * 1024, // 1GB before GC
+ gc_interval: std::time::Duration::from_secs(60 * 60), // hourly
+ gc_min_blocks: 0,
+ gc_target_duration: std::time::Duration::from_secs(60), // 1 minute
+ };
+
+ let nconf = ipfs_embed::NetworkConfig {
+ node_key: libp2p_core::identity::Keypair::generate_ed25519(),
+ node_name: String::from("distrox-devel"),
+ enable_mdns: false, // don't know what this is, yet
+ enable_kad: false, // don't know what this is, yet
+ allow_non_globals_in_dht: false, // don't know what this is, yet
+ psk: None, // Pre shared key for pnet.
+ ping: libp2p_ping::PingConfig::new(), // Ping config.
+ gossipsub: libp2p_gossipsub::GossipsubConfig::default(), // Gossipsub config.
+ bitswap: ipfs_embed::BitswapConfig::new(), // Bitswap config.
+ };
+
+ let ipfs_configuration = ipfs_embed::Config {
+ storage: sconf,
+ network: nconf,
+ };
+ crate::backend::IpfsEmbedBackend::new_with_config(ipfs_configuration).await?
+ };
+
+ backend.ipfs().listen_on("/ip4/127.0.0.1/tcp/0".parse()?).await?;
+
+ match app.get_matches().subcommand() {
+ ("create-profile", Some(mtch)) => {
+ let payload = mtch
+ .value_of("content")
+ .map(String::from)
+ .map(crate::backend::Payload::now_from_text)
+ .unwrap(); // Safe by clap
+
+ let payload_cid = backend.write_payload(&payload).await?;
+ let node = crate::backend::Node::new(crate::consts::v1(), vec![], payload_cid);
+
+ let id = backend.put(node).await?;
+
+ println!("id = {:?}", id);
+ Ok(())
+ },
+
+ ("post", Some(mtch)) => {
+ unimplemented!()
+ },
+
+ (other, _) => {
+ unimplemented!()
+ },
+ }
}
+