Skip to content

Commit

Permalink
add wezterm-dynamic crate
Browse files Browse the repository at this point in the history
  • Loading branch information
wez committed May 18, 2022
1 parent 9b5c887 commit 24c6830
Show file tree
Hide file tree
Showing 20 changed files with 2,570 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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 = [
"bidi/generate",
"strip-ansi-escapes",
"wezterm",
"wezterm-dynamic",
"wezterm-gui",
"wezterm-mux-server",
"wezterm-ssh"
Expand Down
14 changes: 14 additions & 0 deletions wezterm-dynamic/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "wezterm-dynamic"
version = "0.1.0"
edition = "2021"

[dependencies]
wezterm-dynamic-derive = { version="0.1", path="derive" }
ordered-float = "3.0"
thiserror = "1.0"
strsim = "0.10"
log = "0.4"

[dev-dependencies]
maplit = "1.0"
12 changes: 12 additions & 0 deletions wezterm-dynamic/derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "wezterm-dynamic-derive"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0"
quote = "1.0.2"
syn = "1.0"
315 changes: 315 additions & 0 deletions wezterm-dynamic/derive/src/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Attribute, Error, Field, Lit, Meta, NestedMeta, Path, Result};

pub struct ContainerInfo {
pub into: Option<Path>,
pub try_from: Option<Path>,
pub debug: bool,
}

pub fn container_info(attrs: &[Attribute]) -> Result<ContainerInfo> {
let mut into = None;
let mut try_from = None;
let mut debug = false;

for attr in attrs {
if !attr.path.is_ident("dynamic") {
continue;
}

let list = match attr.parse_meta()? {
Meta::List(list) => list,
other => return Err(Error::new_spanned(other, "unsupported attribute")),
};

for meta in &list.nested {
match meta {
NestedMeta::Meta(Meta::Path(path)) => {
if path.is_ident("debug") {
debug = true;
continue;
}
}
NestedMeta::Meta(Meta::NameValue(value)) => {
if value.path.is_ident("into") {
if let Lit::Str(s) = &value.lit {
into = Some(s.parse()?);
continue;
}
}
if value.path.is_ident("try_from") {
if let Lit::Str(s) = &value.lit {
try_from = Some(s.parse()?);
continue;
}
}
}
_ => {}
}
return Err(Error::new_spanned(meta, "unsupported attribute"));
}
}

Ok(ContainerInfo {
into,
try_from,
debug,
})
}

pub enum DefValue {
None,
Default,
Path(Path),
}

pub struct FieldInfo<'a> {
pub field: &'a Field,
pub name: String,
pub skip: bool,
pub flatten: bool,
pub allow_default: DefValue,
pub into: Option<Path>,
pub try_from: Option<Path>,
}

impl<'a> FieldInfo<'a> {
pub fn to_dynamic(&self) -> TokenStream {
let name = &self.name;
let ident = &self.field.ident;
if self.skip {
quote!()
} else if self.flatten {
quote!(
self.#ident.place_dynamic(place);
)
} else if let Some(into) = &self.into {
quote!(
let target : #into = (&self.#ident).into();
place.insert(#name.to_dynamic(), target.to_dynamic());
)
} else {
quote!(
place.insert(#name.to_dynamic(), self.#ident.to_dynamic());
)
}
}

pub fn from_dynamic(&self, struct_name: &str) -> TokenStream {
let name = &self.name;
let ident = &self.field.ident;
let ty = &self.field.ty;
if self.skip {
quote!()
} else if self.flatten {
quote!(
#ident:
<#ty>::from_dynamic(value, options)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?,
)
} else if let Some(try_from) = &self.try_from {
match &self.allow_default {
DefValue::Default => {
quote!(
#ident: match obj.get_by_str(#name) {
Some(v) => {
use std::convert::TryFrom;
let target = <#try_from>::from_dynamic(v, options)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?;
<#ty>::try_from(target)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?
}
None => {
<#ty>::default()
}
},
)
}
DefValue::Path(default) => {
quote!(
#ident: match obj.get_by_str(&#name) {
Some(v) => {
use std::convert::TryFrom;
let target = <#try_from>::from_dynamic(v, options)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?;
<#ty>::try_from(target)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?
}
None => {
#default()
}
},
)
}
DefValue::None => {
quote!(
#ident: {
use std::convert::TryFrom;
let target = <#try_from>::from_dynamic(obj.get_by_str(#name).unwrap_or(&Value::Null), options)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?;
<#ty>::try_from(target)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?
},
)
}
}
} else {
match &self.allow_default {
DefValue::Default => {
quote!(
#ident: match obj.get_by_str(#name) {
Some(v) => {
<#ty>::from_dynamic(v, options)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?
}
None => {
<#ty>::default()
}
},
)
}
DefValue::Path(default) => {
quote!(
#ident: match obj.get_by_str(#name) {
Some(v) => {
<#ty>::from_dynamic(v, options)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?
}
None => {
#default()
}
},
)
}
DefValue::None => {
quote!(
#ident:
<#ty>::from_dynamic(
obj.get_by_str(#name).unwrap_or(&Value::Null),
options
)
.map_err(|source| wezterm_dynamic::Error::ErrorInField {
type_name: #struct_name,
field_name: #name,
error: source.to_string(),
})?,
)
}
}
}
}
}

pub fn field_info(field: &Field) -> Result<FieldInfo> {
let mut name = field.ident.as_ref().unwrap().to_string();
let mut skip = false;
let mut flatten = false;
let mut allow_default = DefValue::None;
let mut try_from = None;
let mut into = None;

for attr in &field.attrs {
if !attr.path.is_ident("dynamic") {
continue;
}

let list = match attr.parse_meta()? {
Meta::List(list) => list,
other => return Err(Error::new_spanned(other, "unsupported attribute")),
};

for meta in &list.nested {
match meta {
NestedMeta::Meta(Meta::NameValue(value)) => {
if value.path.is_ident("rename") {
if let Lit::Str(s) = &value.lit {
name = s.value();
continue;
}
}
if value.path.is_ident("default") {
if let Lit::Str(s) = &value.lit {
allow_default = DefValue::Path(s.parse()?);
continue;
}
}
if value.path.is_ident("into") {
if let Lit::Str(s) = &value.lit {
into = Some(s.parse()?);
continue;
}
}
if value.path.is_ident("try_from") {
if let Lit::Str(s) = &value.lit {
try_from = Some(s.parse()?);
continue;
}
}
}
NestedMeta::Meta(Meta::Path(path)) => {
if path.is_ident("skip") {
skip = true;
continue;
}
if path.is_ident("flatten") {
flatten = true;
continue;
}
if path.is_ident("default") {
allow_default = DefValue::Default;
continue;
}
}
_ => {}
}
return Err(Error::new_spanned(meta, "unsupported attribute"));
}
}

Ok(FieldInfo {
field,
name,
skip,
flatten,
allow_default,
try_from,
into,
})
}
16 changes: 16 additions & 0 deletions wezterm-dynamic/derive/src/bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use proc_macro2::TokenStream;
use syn::{parse_quote, Generics, WhereClause, WherePredicate};

pub fn where_clause_with_bound(generics: &Generics, bound: TokenStream) -> WhereClause {
let new_predicates = generics.type_params().map::<WherePredicate, _>(|param| {
let param = &param.ident;
parse_quote!(#param : #bound)
});

let mut generics = generics.clone();
generics
.make_where_clause()
.predicates
.extend(new_predicates);
generics.where_clause.unwrap()
}
Loading

0 comments on commit 24c6830

Please sign in to comment.