feat(tvix/nix-compat): Add nix serialization support

This change implements the serialization part that is needed to
implement the nix daemon protocol. Previously was add deserialization
and derivers for that and this then adds the other part of that equation
so that you can write types that can then be read using deserialization.

Change-Id: I2917de634980a93822a4f5a8ad38897b9ce16d89
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12729
Autosubmit: Brian Olsen <me@griff.name>
Reviewed-by: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
This commit is contained in:
Brian Olsen 2024-11-03 20:42:01 +01:00 committed by clbot
parent 6582fa69f1
commit b88579ade4
20 changed files with 2339 additions and 15 deletions

View file

@ -3,7 +3,9 @@ use syn::meta::ParseNestedMeta;
use syn::parse::Parse;
use syn::{parse_quote, Attribute, Expr, ExprLit, ExprPath, Lit, Token};
use super::symbol::{Symbol, CRATE, DEFAULT, FROM, FROM_STR, NIX, TRY_FROM, VERSION};
use super::symbol::{
Symbol, CRATE, DEFAULT, DISPLAY, FROM, FROM_STR, INTO, NIX, TRY_FROM, TRY_INTO, VERSION,
};
use super::Context;
#[derive(Debug, PartialEq, Eq)]
@ -104,6 +106,9 @@ pub struct Container {
pub from_str: Option<syn::Path>,
pub type_from: Option<syn::Type>,
pub type_try_from: Option<syn::Type>,
pub type_into: Option<syn::Type>,
pub type_try_into: Option<syn::Type>,
pub display: Default,
pub crate_path: Option<syn::Path>,
}
@ -113,6 +118,9 @@ impl Container {
let mut type_try_from = None;
let mut crate_path = None;
let mut from_str = None;
let mut type_into = None;
let mut type_try_into = None;
let mut display = Default::None;
for attr in attrs {
if attr.path() != NIX {
@ -125,6 +133,18 @@ impl Container {
type_try_from = parse_lit(ctx, &meta, TRY_FROM)?;
} else if meta.path == FROM_STR {
from_str = Some(meta.path);
} else if meta.path == INTO {
type_into = parse_lit(ctx, &meta, INTO)?;
} else if meta.path == TRY_INTO {
type_try_into = parse_lit(ctx, &meta, TRY_INTO)?;
} else if meta.path == DISPLAY {
if meta.input.peek(Token![=]) {
if let Some(path) = parse_lit(ctx, &meta, DISPLAY)? {
display = Default::Path(path);
}
} else {
display = Default::Default(meta.path);
}
} else if meta.path == CRATE {
crate_path = parse_lit(ctx, &meta, CRATE)?;
} else {
@ -144,6 +164,9 @@ impl Container {
from_str,
type_from,
type_try_from,
type_into,
type_try_into,
display,
crate_path,
}
}
@ -341,6 +364,46 @@ mod test {
);
}
#[test]
fn parse_container_from_str() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(from_str)])];
let ctx = Context::new();
let container = Container::from_ast(&ctx, &attrs);
ctx.check().unwrap();
assert_eq!(
container,
Container {
from_str: Some(parse_quote!(from_str)),
type_from: None,
type_try_from: None,
type_into: None,
type_try_into: None,
display: Default::None,
crate_path: None,
}
);
}
#[test]
fn parse_container_from() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(from="u64")])];
let ctx = Context::new();
let container = Container::from_ast(&ctx, &attrs);
ctx.check().unwrap();
assert_eq!(
container,
Container {
from_str: None,
type_from: Some(parse_quote!(u64)),
type_try_from: None,
type_into: None,
type_try_into: None,
display: Default::None,
crate_path: None,
}
);
}
#[test]
fn parse_container_try_from() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(try_from="u64")])];
@ -353,6 +416,89 @@ mod test {
from_str: None,
type_from: None,
type_try_from: Some(parse_quote!(u64)),
type_into: None,
type_try_into: None,
display: Default::None,
crate_path: None,
}
);
}
#[test]
fn parse_container_into() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(into="u64")])];
let ctx = Context::new();
let container = Container::from_ast(&ctx, &attrs);
ctx.check().unwrap();
assert_eq!(
container,
Container {
from_str: None,
type_from: None,
type_try_from: None,
type_into: Some(parse_quote!(u64)),
type_try_into: None,
display: Default::None,
crate_path: None,
}
);
}
#[test]
fn parse_container_try_into() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(try_into="u64")])];
let ctx = Context::new();
let container = Container::from_ast(&ctx, &attrs);
ctx.check().unwrap();
assert_eq!(
container,
Container {
from_str: None,
type_from: None,
type_try_from: None,
type_into: None,
type_try_into: Some(parse_quote!(u64)),
display: Default::None,
crate_path: None,
}
);
}
#[test]
fn parse_container_display() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(display)])];
let ctx = Context::new();
let container = Container::from_ast(&ctx, &attrs);
ctx.check().unwrap();
assert_eq!(
container,
Container {
from_str: None,
type_from: None,
type_try_from: None,
type_into: None,
type_try_into: None,
display: Default::Default(parse_quote!(display)),
crate_path: None,
}
);
}
#[test]
fn parse_container_display_path() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(display="Path::display")])];
let ctx = Context::new();
let container = Container::from_ast(&ctx, &attrs);
ctx.check().unwrap();
assert_eq!(
container,
Container {
from_str: None,
type_from: None,
type_try_from: None,
type_into: None,
type_try_into: None,
display: Default::Path(parse_quote!(Path::display)),
crate_path: None,
}
);

View file

@ -154,10 +154,6 @@ impl<'a> Remote<'a> {
input: &'a inputs::RemoteInput,
) -> Option<Remote<'a>> {
let attrs = attrs::Container::from_ast(ctx, &input.attrs);
if attrs.from_str.is_none() && attrs.type_from.is_none() && attrs.type_try_from.is_none() {
ctx.error_spanned(input, "Missing from_str, from or try_from attribute");
return None;
}
Some(Remote {
ty: &input.ident,
attrs,

View file

@ -11,6 +11,9 @@ pub const DEFAULT: Symbol = Symbol("default");
pub const FROM: Symbol = Symbol("from");
pub const TRY_FROM: Symbol = Symbol("try_from");
pub const FROM_STR: Symbol = Symbol("from_str");
pub const INTO: Symbol = Symbol("into");
pub const TRY_INTO: Symbol = Symbol("try_into");
pub const DISPLAY: Symbol = Symbol("display");
pub const CRATE: Symbol = Symbol("crate");
impl PartialEq<Symbol> for Path {