Skip to content

Commit

Permalink
update to nightly (until unsize stabilized)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewgazelka committed Apr 2, 2023
1 parent 417c39a commit 8e10b50
Show file tree
Hide file tree
Showing 13 changed files with 390 additions and 100 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/rust-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ jobs:
-A clippy::match-bool
# - uses: taiki-e/install-action@cargo-llvm-cov

- name: Install cargo-llvm-cov
run: cargo install --git https://github.com/andrewgazelka/cargo-llvm-cov --branch region-coverage-codecov cargo-llvm-cov

- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest

- name: Collect coverage data
Expand Down
39 changes: 38 additions & 1 deletion Cargo.lock

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

59 changes: 37 additions & 22 deletions code/derive-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ extern crate proc_macro;

use inflector::string::singularize::to_singular;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Path, Type, TypePath};
use quote::{quote};
use syn::{parse_macro_input, DeriveInput, Meta, Path, Type, TypePath};

#[proc_macro_derive(Build, attributes(required))]
#[proc_macro_derive(Build, attributes(required, default))]
pub fn build_macro_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
impl_build_macro(&ast)
Expand Down Expand Up @@ -50,6 +50,28 @@ fn impl_build_macro(ast: &DeriveInput) -> TokenStream {
let name = &ast.ident;
let (required_fields, optional_fields) = partition_fields(&ast.data);

let (optional_fields, optional_defaults): (Vec<_>, Vec<_>) = optional_fields
.iter()
.map(|field| {
let default_value = field
.attrs
.iter()
.find(|attr| attr.path().is_ident("default"))
.map(|attr| {
let Meta::NameValue(v)= &attr.meta else {
panic!("only named values allowed for default attribute")
};

let v= &v.value;

quote!(#v)
})
.unwrap_or_else(|| quote! { Default::default() });

(field, default_value)
})
.unzip();

let required_params = required_fields.iter().map(|field| {
let field_name = &field.ident;
let field_type = &field.ty;
Expand Down Expand Up @@ -116,28 +138,21 @@ fn impl_build_macro(ast: &DeriveInput) -> TokenStream {
}
});

let expanded = match required_params.len() == 0 {
true => quote! {
impl #name {
pub fn new() -> Self {
Default::default()
}
let optional_field_idents = optional_fields.iter().map(|field| &field.ident);

#(#optional_methods)*
}
},
false => quote! {
impl #name {
pub fn new(#(#required_params),*) -> Self {
Self {
#(#required_assignments,)*
..Default::default()
}
let expanded = quote! {
impl #name {
pub fn new(#(#required_params),*) -> Self {
Self {
#(#required_assignments,)*
#(
#optional_field_idents: #optional_defaults,
)*
}

#(#optional_methods)*
}
},

#(#optional_methods)*
}
};

TokenStream::from(expanded)
Expand Down
154 changes: 96 additions & 58 deletions code/derive-discriminant/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,80 +12,118 @@ pub fn discriminant_derive(input: TokenStream) -> TokenStream {

fn impl_discriminant_macro(ast: DeriveInput) -> TokenStream {
let name = &ast.ident;
let attrs = ast.attrs;

if let Data::Enum(data_enum) = ast.data {
let variant_impls = data_enum.variants.into_iter().map(|variant| {
let variant_name = &variant.ident;
let fields = &variant.fields;

match fields {
Fields::Unit => {
quote! {
impl From<#variant_name> for #name {
fn from(value: #variant_name) -> Self {
Self::#variant_name
}

// all non-doc attributes
let global_attrs: Vec<_> = ast
.attrs
.into_iter()
.filter(|attr| !attr.path().is_ident("doc"))
.collect();

let Data::Enum(data_enum) = ast.data else {
panic!("Discriminant can only be derived for enums");
};

let variant_names: Vec<_> = data_enum
.variants
.iter()
.map(|variant| &variant.ident)
.collect();

// implementation for the .cast() method to cast into a trait object
// this requires nightly
let cast = quote! {
impl #name {
fn cast<U: ?Sized>(self) -> Box<U> where #(#variant_names: ::core::marker::Unsize<U>),* {
let value = self;
// TODO: use a singular match expression
#(
let value = match #variant_names::try_from(value) {
Ok(v) => {
let x = Box::new(v);
return x;
}
Err(v) => v,
};
)*

impl std::convert::TryFrom<#name> for #variant_name {
type Error = ();
unreachable!();
}
}
};

fn try_from(value: #name) -> Result<Self, Self::Error> {
if let #name::#variant_name = value {
Ok(#variant_name)
} else {
Err(())
}
}
let variant_impls = data_enum.variants.into_iter().map(|variant| {
let variant_name = &variant.ident;
let fields = &variant.fields;
let variant_attrs = variant.attrs;

match fields {
Fields::Unit => {
quote! {
impl From<#variant_name> for #name {
fn from(value: #variant_name) -> Self {
Self::#variant_name
}
}

impl std::convert::TryFrom<#name> for #variant_name {
type Error = #name;

#(#attrs)*
struct #variant_name;
fn try_from(value: #name) -> Result<Self, Self::Error> {
if let #name::#variant_name = value {
Ok(#variant_name)
} else {
Err(value)
}
}
}

#(#global_attrs)*
#(#variant_attrs)*
struct #variant_name;
}
_ => {
let field_name = fields.iter().map(|field| &field.ident).collect::<Vec<_>>();
let field_type = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();

quote! {
impl From<#variant_name> for #name {
fn from(value: #variant_name) -> Self {
Self::#variant_name {
#(#field_name: value.#field_name),*
}
}
_ => {
let field_name = fields.iter().map(|field| &field.ident).collect::<Vec<_>>();
let field_type = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();

quote! {
impl From<#variant_name> for #name {
fn from(value: #variant_name) -> Self {
Self::#variant_name {
#(#field_name: value.#field_name),*
}
}
}

impl std::convert::TryFrom<#name> for #variant_name {
type Error = #name;

impl std::convert::TryFrom<#name> for #variant_name {
type Error = ();

fn try_from(value: #name) -> Result<Self, Self::Error> {
if let #name::#variant_name { #(#field_name),* } = value {
Ok(#variant_name {
#(#field_name),*
})
} else {
Err(())
}
fn try_from(value: #name) -> Result<Self, Self::Error> {
if let #name::#variant_name { #(#field_name),* } = value {
Ok(#variant_name {
#(#field_name),*
})
} else {
Err(value)
}
}
}

#(#attrs)*
struct #variant_name {
#(#field_name: #field_type),*
}
#(#global_attrs)*
#(#variant_attrs)*
struct #variant_name {
#(#field_name: #field_type),*
}
}
}
});
}
});

let output = quote! {
#(#variant_impls)*
};
let output = quote! {
#(#variant_impls)*
#cast
};

TokenStream::from(output)
} else {
panic!("Discriminant can only be derived for enums.")
}
TokenStream::from(output)
}
2 changes: 2 additions & 0 deletions code/derive-discriminant/tests/macro.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(unsize)]

use derive_discriminant::Discriminant;

#[derive(Discriminant)]
Expand Down
7 changes: 7 additions & 0 deletions code/executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.70"
async-trait = "0.1.68"
derive-discriminant.workspace = true
derive-build.workspace = true
ron = "0.8.0"
tokio = { version = "1.27.0", features = ["full"] }
utils.workspace = true
35 changes: 35 additions & 0 deletions code/executor/src/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Commands are executed as such
//!
//! ```text
//! {cmd header}
//! {args}
//! ```
//!
//! where {cmd data} is one line of RON
//! but {args} can be several lines

use async_trait::async_trait;
use derive_discriminant::Discriminant;

use crate::Ctx;

mod bash;
mod zsh;

/// The command we are executing
#[derive(Discriminant)]
enum Cmd {
/// a zsh script to execute
Zsh,
Bash,
}

#[async_trait]
trait Command {
async fn execute(&self, ctx: Ctx, input: &str) -> anyhow::Result<String>;
}

fn this_requires_unsize() {
let cmd1: Box<dyn Command> = Cmd::Zsh.cast();
let cmd2: Box<dyn Command> = Cmd::Bash.cast();
}
Loading

0 comments on commit 8e10b50

Please sign in to comment.