Skip to content

Commit

Permalink
Merge pull request #26 from kcl-lang/feat-pure-rust-sdk
Browse files Browse the repository at this point in the history
feat: add KCL pure rust sdk
  • Loading branch information
Peefy authored Dec 6, 2023
2 parents 7a72f43 + f3ae68b commit 17f477a
Show file tree
Hide file tree
Showing 33 changed files with 1,641 additions and 3 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI
on:
pull_request:
push:
branches:
- main
- "releases/*"
jobs:
build-and-test:
# Ref: https://github.com/actions/runner-images/tree/main/images/macos
strategy:
matrix:
os: [macos-11, macos-12, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Git checkout
uses: actions/checkout@v2
with:
submodules: "true"

- name: Install rust nightly toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: 1.73
override: true
components: clippy, rustfmt
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19

- name: Rust code test
run: cargo test
- name: Go code test
run: go test ./...
38 changes: 38 additions & 0 deletions .github/workflows/cla.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: "CLA Assistant"
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened,closed,synchronize]
jobs:
CLAssistant:
runs-on: ubuntu-latest
steps:
- name: "CLA Assistant"
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
uses: contributor-assistant/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# the below token should have repo scope and must be manually added by you in the repository's secret
PERSONAL_ACCESS_TOKEN : ${{ secrets.DEPLOY_ACCESS_TOKEN }}
with:
path-to-document: 'https://github.com/kcl-lang/.github/blob/main/CLA.md' # e.g. a CLA or a DCO document

# branch should not be protected
lock-pullrequest-aftermerge: True
path-to-signatures: 'signatures/version1/cla.json'
remote-organization-name: kcl-lang
remote-repository-name: cla.db
branch: 'main'
allowlist: bot*

#below are the optional inputs - If the optional inputs are not given, then default values will be taken
#remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository)
#remote-repository-name: enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository)
#create-file-commit-message: 'For example: Creating file for storing CLA Signatures'
#signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo'
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
#use-dco-flag: true - If you are using DCO instead of CLA
14 changes: 13 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ zz_*
*.lock
.kclvm

# KCL cache and temp output
.kclvm
.kclvm_cov
*.dylib
*.so
*.dll
*.lock
*.ll
*.ll.lock
_a.out.*
_a.out_*.*

**/target/
**/.DS_Store
**/.vscode
Expand All @@ -25,4 +37,4 @@ __pycache__
!lib/**/*.lib
!lib/**/*.so
!lib/**/*.exe
!scripts/*.exe
!scripts/*.exe
15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "kcl-lang"
version = "0.7.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
prost = "0.11.8"
prost-types = "0.11.8"
serde_json = "1"
anyhow = "1"
serde = { version = "1", features = ["derive"] }
libloading = "0.7.3"
tempfile = "3.5.0"
53 changes: 51 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,52 @@
# kcl-artifact-go
# KCL Artifact for SDKs

Embed kcl artifact for go
## Rust

### How to Use

```shell
cargo add kcl
```

Write the Code

```rust
use kcl_lang::*;
use std::path::Path;
use anyhow::Result;

fn main() -> Result<()> {
let api = API::new()?;
let args = &ExecProgramArgs {
work_dir: Path::new(".").join("src").join("testdata").canonicalize().unwrap().display().to_string(),
k_filename_list: vec!["test.k".to_string()],
..Default::default()
};
let exec_result = api.exec_program(args)?;
assert_eq!(exec_result.yaml_result, "alice:\n age: 18");
Ok(())
}
```

## Go

### How to Use

```shell
go get kcl-lang.io/kcl-artifact-go
```

Write the Code

```go
package main

import (
artifact "kcl-lang.io/kcl-artifact-go"
)

func main() {
path = "path/to/install"
_ := artifact.InstallKclvm(path)
}
```
30 changes: 30 additions & 0 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# KCL Rust SDK

KCL Rust SDK

### How to Use

```shell
cargo add kcl
```

Write the Code

```rust
use kcl_lang::*;
use std::path::Path;
use anyhow::Result;

fn main() -> Result<()> {
// File case
let api = API::new()?;
let args = &ExecProgramArgs {
work_dir: Path::new(".").join("src").join("testdata").canonicalize().unwrap().display().to_string(),
k_filename_list: vec!["test.k".to_string()],
..Default::default()
};
let exec_result = api.exec_program(args)?;
assert_eq!(exec_result.yaml_result, "alice:\n age: 18");
Ok(())
}
```
142 changes: 142 additions & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
mod service;
mod spec;
use crate::bootstrap::{install_kclvm, lib_full_name};
use anyhow::Result;
use prost::Message;
use serde::de::DeserializeOwned;
pub use service::Service;
use service::ServiceHandler;
pub use spec::*;
use std::{
ffi::{c_char, CStr, CString},
sync::Mutex,
};
use tempfile::TempDir;

#[derive(Debug)]
pub struct API {
caller: Caller,
}

#[derive(Debug)]
struct Caller {
_dir: TempDir,
lib: libloading::Library,
mutex: Mutex<i32>,
handler: *mut ServiceHandler,
}

impl Caller {
fn new() -> Result<Self> {
unsafe {
let root = tempfile::tempdir()?;
install_kclvm(&root)?;
let lib = libloading::Library::new(&root.path().join("bin").join(lib_full_name()))?;
let service_new: libloading::Symbol<
unsafe extern "C" fn(plugin_agent: u64) -> *mut ServiceHandler,
> = lib.get(b"kclvm_service_new")?;
let handler = service_new(0);
Ok(Self {
_dir: root,
mutex: Mutex::new(0i32),
lib,
handler,
})
}
}

fn call<A, R>(&self, name: &str, args: &A) -> Result<R>
where
A: Message + DeserializeOwned,
R: Message + Default + PartialEq + DeserializeOwned + serde::Serialize,
{
let _lock = self.mutex.lock().unwrap();
let result_ptr = unsafe {
let args = CString::from_vec_unchecked(args.encode_to_vec());
let call = CString::new(name)?;
let service_call: libloading::Symbol<
unsafe extern "C" fn(
serv: *mut ServiceHandler,
call: *const c_char,
args: *const c_char,
) -> *const c_char,
> = self.lib.get(b"kclvm_service_call")?;

service_call(self.handler, call.as_ptr(), args.as_ptr()) as *mut i8
};
let result = unsafe { CStr::from_ptr(result_ptr) };
match R::decode(result.to_bytes()) {
Ok(result) => Ok(result),
Err(_) => Err(anyhow::anyhow!(result.to_str()?.to_owned())),
}
}
}

impl Drop for Caller {
fn drop(&mut self) {
unsafe {
let delete: libloading::Symbol<unsafe extern "C" fn(*mut ServiceHandler)> =
self.lib.get(b"kclvm_service_delete").unwrap();
delete(self.handler);
}
}
}

impl API {
pub fn new() -> Result<Self> {
let caller = Caller::new()?;
Ok(Self { caller })
}
}

impl Service for API {
fn exec_program(&self, args: &ExecProgramArgs) -> anyhow::Result<ExecProgramResult> {
self.caller.call("KclvmService.ExecProgram", args)
}

fn override_file(&self, args: &OverrideFileArgs) -> anyhow::Result<OverrideFileResult> {
self.caller.call("KclvmService.OverrideFile", args)
}

fn get_full_schema_type(
&self,
args: &GetFullSchemaTypeArgs,
) -> anyhow::Result<GetSchemaTypeResult> {
self.caller.call("KclvmService.GetFullSchemaType", args)
}

fn format_code(&self, args: &FormatCodeArgs) -> anyhow::Result<FormatCodeResult> {
self.caller.call("KclvmService.FormatCode", args)
}

fn format_path(&self, args: &FormatPathArgs) -> anyhow::Result<FormatPathResult> {
self.caller.call("KclvmService.FormatPath", args)
}

fn lint_path(&self, args: &LintPathArgs) -> anyhow::Result<LintPathResult> {
self.caller.call("KclvmService.LintPath", args)
}

fn validate_code(&self, args: &ValidateCodeArgs) -> anyhow::Result<ValidateCodeResult> {
self.caller.call("KclvmService.ValidateCode", args)
}

fn load_settings_files(
&self,
args: &LoadSettingsFilesArgs,
) -> anyhow::Result<LoadSettingsFilesResult> {
self.caller.call("KclvmService.LoadSettingsFiles", args)
}

fn rename(&self, args: &RenameArgs) -> anyhow::Result<RenameResult> {
self.caller.call("KclvmService.Rename", args)
}

fn rename_code(&self, args: &RenameCodeArgs) -> anyhow::Result<RenameCodeResult> {
self.caller.call("KclvmService.RenameCode", args)
}

fn test(&self, args: &TestArgs) -> anyhow::Result<TestResult> {
self.caller.call("KclvmService.Test", args)
}
}
Loading

0 comments on commit 17f477a

Please sign in to comment.