Skip to content

Commit

Permalink
feat(conf): add cln conf parser crate
Browse files Browse the repository at this point in the history
Signed-off-by: Vincenzo Palazzo <[email protected]>
  • Loading branch information
vincenzopalazzo committed Jan 5, 2023
1 parent db503bc commit 6098b98
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"common",
"plugin",
"plugin_macros",
"conf",
]
resolver = "2"

1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ fmt:

check:
$(CC) test --all
$(CC) clippy --all

example:
$(CC) build --example foo_plugin
Expand Down
14 changes: 14 additions & 0 deletions conf/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "clightningrpc-conf"
version = "0.0.1-alpha.1"
edition = "2021"
authors = ["Vincenzo Palazzo <[email protected]>"]
license = "CC0-1.0"
homepage = "https://github.com/laanwj/cln4rust"
repository = "https://github.com/laanwj/cln4rust.git"
description = "Crate that provides a simple configuration manager for core lightning"
keywords = [ "cln-conf-parser", "cln", "rpc", "lightning", "bitcoin" ]
readme = "README.md"

[dependencies]
albert_stream = "0.0.3"
41 changes: 41 additions & 0 deletions conf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<div align="center">
<h1>Rust c-lightning conf manager</h1>

<p>
<strong>This crate provides configuration manager for core lightning.</strong>
</p>

<p>
</p>

<h4>
<a href="https://github.com/laanwj/rust-clightning-rpc">Project Homepage</a>
</h4>

<a href="https://github.com/laanwj/rust-clightning-rpc/actions">
<img alt="GitHub Workflow Status (branch)" src="https://img.shields.io/github/workflow/status/laanwj/rust-clightning-rpc/Integration%20testing/master?style=flat-square"/>
</a>

<a href="https://crates.io/clightningrpc">
<img alt="Crates.io" src="https://img.shields.io/crates/v/clightningrpc-conf?style=flat-square"/>
</a>

<a href="https://docs.rs/clightningrpc">
<img alt="docs.rs" src="https://img.shields.io/docsrs/clightningrpc-conf?style=flat-square"/>
</a>

</div>

This crate provides an simple configuration manager for [core lightning](https://github.com/ElementsProject/lightning).
# Contributing guidelines

- Four spaces
- Call `make fmt` before committing
- If you can, GPG-sign at least your top commit when filing a PR

# Supports

If you want support this library consider to donate with the following methods

- Lightning address: [email protected]
- [Github donation](https://github.com/sponsors/vincenzopalazzo)
38 changes: 38 additions & 0 deletions conf/src/file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! file implementation to read and write content
use std::fs;
use std::io::{Error, Write};

pub trait SyncFile {
/// sync version to write into the file
fn write(&self, buff: &str) -> Result<(), Error> {
let mut writer = fs::File::create(self.path())?;
write!(writer, "{buff}")?;
Ok(())
}

/// sync version to read the content of the file
fn read(&self) -> Result<String, Error> {
let content = fs::read_to_string(self.path())?;
Ok(content)
}

fn path(&self) -> String;
}

pub struct File {
path: String,
}

impl File {
pub fn new(path: &str) -> Self {
File {
path: path.to_owned(),
}
}
}

impl SyncFile for File {
fn path(&self) -> String {
self.path.to_owned()
}
}
100 changes: 100 additions & 0 deletions conf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! Core lightning configuration manager written in rust.
use std::{collections::HashMap, rc::Rc};

mod file;
mod parser;

pub type ParsingError = String;

pub trait SyncCLNConf {
fn parse(&mut self, path: &str) -> Result<(), ParsingError>;
}

/// core lightning configuration manager
/// that help to parser and create a core
/// lightning configuration with rust.
pub struct CLNConf {
/// collection of field included
/// inside the conf file.
///
/// `plugin=path/to/bin` is parser as
/// `key=value`.
pub filed: HashMap<String, String>,
/// other conf file included.
pub includes: Vec<Rc<CLNConf>>,
}

impl CLNConf {
/// create a new instance of the configuration
/// file manager.
pub fn new() -> Self {
CLNConf {
filed: HashMap::new(),
includes: Vec::new(),
}
}

/// build a new instance of the parser.
pub fn parser(&self, path: &str) -> parser::Parser {
parser::Parser::new(path)
}

pub fn add_conf(&mut self, key: &str, val: &str) {
self.filed.insert(key.to_owned(), val.to_owned());
}

pub fn add_subconf(&mut self, conf: CLNConf) {
self.includes.push(conf.into());
}
}

impl SyncCLNConf for CLNConf {
fn parse(&mut self, path: &str) -> Result<(), ParsingError> {
let parser = self.parser(path);
if let Err(err) = parser.parse(self) {
return Err(err.to_string());
}
Ok(())
}
}

impl Default for CLNConf {
fn default() -> Self {
CLNConf::new()
}
}

#[cfg(test)]
mod tests {
use std::env;
use std::fs::{remove_file, File};
use std::io::Write;

use crate::{CLNConf, SyncCLNConf};

fn build_file(content: &str) -> Result<String, std::io::Error> {
let binding = env::temp_dir();
let dir = binding.as_os_str().to_str().unwrap();
let conf = format!("{dir}/conf");
let mut file = File::create(conf.clone())?;
write!(file, "{content}")?;
Ok(conf)
}

fn cleanup_file(path: &str) {
remove_file(path).unwrap();
}

#[test]
fn parsing_key_value_one() {
let path = build_file("plugin=foo\nnetwork=bitcoin");
assert!(path.is_ok());
let path = path.unwrap();
let mut conf = CLNConf::new();
let result = conf.parse(path.as_str());
assert!(result.is_ok());
assert_eq!(conf.filed.keys().len(), 2);

cleanup_file(path.as_str());
}
}
66 changes: 66 additions & 0 deletions conf/src/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! Parser implementation to parse the simple
//! conf syntax for core lightning conf file
use crate::{
file::{File, SyncFile},
CLNConf, ParsingError, SyncCLNConf,
};
use albert_stream::{BasicStream, Stream};

pub struct Parser {
file: File,
}

type Word = String;

impl Parser {
pub(crate) fn new(file_path: &str) -> Self {
Parser {
file: File::new(file_path),
}
}

fn read_and_split(&self) -> Result<Vec<Word>, ParsingError> {
let content = self.file.read().unwrap();
let words: Vec<Word> = content
.split('=')
.map(|it| it.to_string())
.collect::<Vec<String>>();
Ok(words)
}

pub fn parse(&self, conf: &mut CLNConf) -> Result<(), ParsingError> {
let words = self.read_and_split()?;
let mut stream = BasicStream::<Word>::new(&words);
self.parse_stream(&mut stream, conf)
}

fn parse_stream(
&self,
stream: &mut BasicStream<Word>,
conf: &mut CLNConf,
) -> Result<(), ParsingError> {
while !stream.is_end() {
self.parse_key_value(stream, conf)?;
}
Ok(())
}

fn parse_key_value(
&self,
stream: &mut BasicStream<Word>,
conf: &mut CLNConf,
) -> Result<(), ParsingError> {
let key = stream.advance().to_owned();
let value = stream.advance();
if key == "include" {
let mut subconf = CLNConf::new();
if let Err(err) = subconf.parse(value) {
return Err(err);
}
conf.add_subconf(subconf);
} else {
conf.add_conf(&key, value);
}
Ok(())
}
}

0 comments on commit 6098b98

Please sign in to comment.