جاسازی KCL در Rust – Dev Community

KCL چیست؟
KCL یک زبان پیکربندی جدید و جالب است.
کمی شبیه مخلوطی از yaml و hcl است.
# This is a KCL document
title = "KCL Example"
owner = {
name = "The KCL Authors"
date = "2020-01-02T03:04:05"
}
database = {
enabled = True
ports = [8000, 8001, 8002]
data = [["delta", "phi"], [3.14]]
temp_targets = {cpu = 79.5, case = 72.0}
}
servers = [
{ip = "10.0.0.1", role = "frontend"}
{ip = "10.0.0.2", role = "backend"}
]
اما آنچه در جدول به ارمغان می آورد این است که KCL اضافه کردن تایپ و اعتبار داده به پیکربندی شما را اضافه می کند.
اعتبار سنجی و تایپ می تواند از طریق داده های شما اعمال شود schema
بشر
schema User:
name: str
age: int
message?: str
data: Data
labels: {str:}
hc: [int]
check:
age > 10, "age must > 10"
schema Data:
id: int
value: int = 9000
با schema
ما می توانیم تعریف کنیم که کدام قسمت ها نوع خاصی از داده ها باید داشته باشند.
علاوه بر این ما می توانیم انواع اضافه کنیم ، مقادیر پیش فرض را تنظیم کنیم و حتی از طریق محدودیت ها را در قسمت ها اعمال کنیم check
بشر
بعد از تعریف ، الف schema
می تواند فوری شود
User {
name = "Foo"
age = 42
data = Data {
id = 0
}
labels = {
bar = 12
baz = "some label"
}
hc = [1,2,3]
}
سپس کد KCL را می توان در JSON یا YAML استاندارد “گردآوری کرد”.
قطعه کد کاربر از بالا “کامپایل” به:
{
"name": "Foo",
"age": 42,
"data": {
"id": 0,
"value": 9000
},
"labels": {
"bar": 12,
"baz": "some label"
},
"hc": [
1,
2,
3
]
}
یا
name: Foo
age: 42
data:
id: 0
value: 9000
labels:
bar: 12
baz: some label
hc:
- 1
- 2
- 3
KCL ویژگی های جالب تری دارد ، مانند جریان کنترل ، ماژول ها و حتی رجیستری برای این ماژول ها ، به طوری که می توان طرحواره ها را به راحتی به اشتراک گذاشته و دوباره استفاده کرد.
اما اگر می خواهید اطلاعات بیشتری در مورد KCL داشته باشید ، فقط به KCL-lang.io مراجعه کنید ، باید به عنوان یک مقدمه KCL کافی باشد.
جاسازی KCL در زنگ زدگی
پس از فهمیدن اینکه خود KCL در Rust نوشته شده است (در بیشتر مثالها KCL را به عنوان یک ابزار CLI می بینید) ، من تصمیم گرفتم که این یک ایده جالب باشد که KCL را در برنامه Rust خود جاسازی کنم تا بتوانید پرونده پیکربندی برنامه را تأیید کنید.
خوشبختانه ، KCL دارای lib
که چندین زبان را ارائه می دهد و به راحتی از طریق می توان اضافه کرد:
cargo add --git https://github.com/kcl-lang/lib kcl-lang
پس از آن ، می توانید KCL را در کد زنگ خود مانند این کامپایل کنید:
let api = API::default();
let args = &ExecProgramArgs {
k_filename_list: vec!["main.k".to_string()],
k_code_list: vec!["a = 1".to_string()],
..Default::default()
};
let exec_result = api.exec_program(args)?;
println!("{}", exec_result.yaml_result);
من فرض می کنم این CLI API است ، زیرا ما باید اضافه کنیم لیست پرونده ها (آنها هیچ ارتباطی برای این مورد استفاده ندارند ، اما شما نمی توانید آنها را پرش کنید) و خروجی stdout & stderr را که CLI با آن تولید می کند دریافت کنید exec_result
بشر
ایده این بود که طرح پیکربندی را به کد منبع اضافه کرده و آن را در طول زمان کامپایل از طریق include_str!()
کلان
طرحواره اینگونه به نظر می رسد:
# schema.k
schema Configuration:
api: API = API {}
gateway: Gateway = Gateway {}
schema API:
port: int = 8150
check:
1 <= port <= 65535, "API Port not in range 1 to 65535"
schema Gateway:
port: int = 80
check:
1 <= port <= 65535, "Gatewayport Port not in range 1 to 65535"
ساختار زنگ زدگی مربوطه:
#[derive(Debug, Serialize, Deserialize)]
pub struct Configuration {
pub api: APIConfiguration,
pub gateway: GatewayConfiguration,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct APIConfiguration {
pub port: u16,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GatewayConfiguration {
pub port: u16,
}
من قصد داشتم کاربران را قادر به نوشتن فایل پیکربندی خود در KCL مانند:
# Long version
api = {
port = 8150
}
# Short version
gateway.port = 8080
سپس آن پرونده پیکربندی را در برابر طرح پیکربندی من که شامل تمام مقادیر پیش فرض لازم است ، کامپایل کنید.
بنابراین من می خواهم محتوای پرونده پیکربندی کاربر را با تعریف طرحواره خود با موارد زیر ترکیب کنم:
# schema.k content
schema Configuration:
api: API = API {}
gateway: Gateway = Gateway {}
schema API:
port: int = 8150
check:
1 <= port <= 65535, "API Port not in range 1 to 65535"
schema Gateway:
port: int = 80
check:
1 <= port <= 65535, "Gatewayport Port not in range 1 to 65535"
Configuration {
# User configuration file content
api = {
port = 8150
}
gateway.port = 8080
}
و سرانجام ، کد زنگ زدگی لازم برای رها کردن ساختار پیکربندی از کد KCL کامپایل شده:
impl Configuration {
pub fn default() -> Result<Self> {
Self::process_kcl(None)
}
pub fn from_configuration_file(path: &Path) -> Result<Self> {
Self::process_kcl(Some(path))
}
fn process_kcl(path: Option<&Path>) -> Result<Self> {
let mut file_name = "default-configuration.k".to_string();
let mut code = String::new();
if let Some(path) = path {
file_name = "configuration.k".to_string();
code = std::fs::read_to_string(path)?;
}
let code = format!("{}\nConfiguration{{\n{}}}", SCHEMA, code);
let args = &ExecProgramArgs {
k_filename_list: vec![file_name],
k_code_list: vec![code],
..Default::default()
};
let api = API::default();
let exec_result = api.exec_program(args)?;
if !exec_result.err_message.is_empty() {
return Err(anyhow!(
"Configuration error:\n\n{}",
exec_result.err_message
));
}
serde_json::from_str(&exec_result.json_result)
.map_err(|err| anyhow!(err))
}
}
بدون ارائه پرونده پیکربندی ، ما در برابر a کامپایل می کنیم Configuration{}
به عنوان مثال ، که سپس تمام مقادیر پیش فرض را از طرح دریافت می کند.
کمی عجیب به نظر می رسد که اگر در برابر برخی از پیکربندی های کاربر که برای طرح ارائه شده معتبر نیست ، نتیجه خطا دریافت نمی کنیم.
تنها راه برای بررسی خطای تلفیقی بررسی اینکه آیا exec_result.err_message
است ، ""
بشر این پیام خطا خروجی STDERR است که در صورت عدم موفقیت ، ابزار CLI تولید می کند.
دست و پا گیر است ، اما حداقل ما با اطلاعات خاص در مورد آنچه در کد پیکربندی KCL اشتباه است ، پیام خطای خوبی دریافت می کنیم.
مرحله آخر پس از آن برای رها کردن Configuration
ساختار از نمایش JSON از نتیجه تلفیقی -> ما می توانیم برنامه زنگ زدگی خود را از طریق پرونده پیکربندی KCL پیکربندی کنیم.
مرحله آخر پس از آن برای رها کردن Configuration
ساختار از نمایش JSON از نتیجه تلفیقی -> ما می توانیم برنامه زنگ زدگی خود را از طریق پرونده پیکربندی KCL پیکربندی کنیم.
پایان
اول فکر: این کار می کند و این یک چیز خوب است!
فکر دوم: کار با CLI API در کد زنگ زدگی به نوعی بی دست و پا است و پس از آن ، مجدداً ساختار ساختار را از نمایندگی JSON یا YAML غیر ضروری به نظر می رسد.
من مطمئن هستم که یک روش باهوش تر برای تجزیه مستقیم KCL و ایجاد ساختار با مقادیر Serde که توسط تجزیه کننده تولید می شود وجود دارد.
متأسفانه مستندات KCL-Lang Lib بسیار کوتاه نگه داشته می شود و من نتوانستم رویکردی مانند آن پیدا کنم.
در مورد یک کلان رویه ای چطور؟
تمام آن کد دیگ بخار نیز می تواند توسط یک کلان رویه ای که برای آن اعمال می شود تولید شود Configuration
ساختار ، شبیه به آنچه به عنوان مثال Serde ، Clap و غیره انجام می شود.
با این وجود ، KCL جالب است
به نظر من پیکربندی KCL یک مکمل عالی برای زنگ زدگی است.
مشابه زنگ زدگی ، KCL از انواع و محدودیت ها استفاده می کند تا احتمال نادرست پیکربندی ما به حداقل برسد.