KFL

  • Nominal Typing
  • Trait-Based

TODO

  • Struct variants and tuple variants
  • DecodePartial compatibility cannot automatically be detected (except Unit useless case)
    • Because we discard option parameter and also switch to manually implementing it for Option<T> and Vec<T>
    • Beautiful fact is that default Decode behaviour anyway coerce T into Option<T> when ChildMode::Normal or Vec<T> when ChildMode::Multi at decoding (and then unwrap or into_iter.collect)
  • Implement DecodeScalar for struct as the replacement of flatten(properties) and support flatten(arguments) equivalent as well
  • Span
  • Detect name conflicts between fields in the same struct
  • Understand error categories, reconsider Context in the presence of DecodePartial
  • Test organisation, provide utility macros
  • Encode
    • EncodePartial as an analogous to skip_serializing_none

Introduction

node0
node1 1 "hoge"
node2 a=1 b="hoge"
structs
#[derive(Decode)]
struct Node0;
#[derive(Decode)]
struct Node1(
    #[kfl(argument)] i32,
    #[kfl(argument)] String
);
#[derive(Decode)]
struct Node2(
    #[kfl(property(name = "a"))] i32,
    #[kfl(property(name = "b"))] String
);
#[derive(Decode)]
struct Node1 {
    #[kfl(argument)] a: i32,
    #[kfl(argument)] b: String
}
#[derive(Decode)]
struct Node2 {
    #[kfl(property)] a: i32,
    #[kfl(property)] b: String
}
enums
                          #[derive(Decode)]
                            enum Node {
                                Node0,
                                Node1(
                                    #[kfl(argument)] i32,
                                    #[kfl(argument)] String
                                ),
                                Node2(
                                    #[kfl(property(name = "a"))] i32,
                                    #[kfl(property(name = "b"))] String
                                )
                            }
                          #[derive(Decode)]
                            enum Node {
                                Node0,
                                Node1 {
                                    #[kfl(argument)] a: i32,
                                    #[kfl(argument)] b: String
                                },
                                Node2 {
                                    #[kfl(property)] a: i32,
                                    #[kfl(property)] b: String
                                }
                            }

Tutorial

Case 1: All Arguments

Case 1.1: With Different Types

node 1 "hoge"

Pattern 1.1.1: Tuple Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node(#[kfl(argument)] i32, #[kfl(argument)] String);
}

Pattern 1.1.2 Struct Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node {
    #[kfl(argument)]
    a: i32,
    #[kfl(argument)]
    b: String
}
}

NOTE: a and b are redundant.

Case 1.2: With The Same Type

node 1 2

Pattern 1.2.1: Tuple Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node(#[kfl(arguments)] Vec<i32>);
}

Pattern 1.2.2 Struct Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node {
    #[kfl(arguments)]
    a: Vec<i32>
}
}

NOTE: a is redundant.

Case 2: All Properties

Case 2.1: With Different Types

node a=1 b="hoge"

Pattern 2.1.1: Tuple Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node(#[kfl(property(name = "a"))] i32, #[kfl(property(name = "b"))] String);
}

NOTE: name = "a" and name = "b" are necessary.

Pattern 2.1.2: Struct Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node {
    #[kfl(property)]
    a: i32,
    #[kfl(property)]
    b: String
}
}

Case 2.2: With The Same Type

node a=1 b=2

Pattern 2.2.1: Tuple Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node(#[kfl(properties)] HashMap<String, i32>);
}

Pattern 2.2.2: Struct Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node {
    #[kfl(properties)]
    a: HashMap<String, i32>
}
}

NOTE: a is redundant.

Case 3: Mix of Singulars and A Plural

Case 3.1: Arguments

node 1 2 3

Pattern 3.1.1: Tuple Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node(#[kfl(argument)] i32, #[kfl(arguments)] Vec<i32>);
}

Pattern 3.1.2: Struct Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node {
    #[kfl(argument)]
    a: i32,
    #[kfl(arguments)]
    b: Vec<i32>
}
}

Case 3.2: Properties

node a=1 b=2 c=3

Pattern 3.2.1: Tuple Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node(#[kfl(property(name = "a")] i32, #[kfl(properties)] HashMap<String, i32>);
}

NOTE: name = "a" is necessary.

Pattern 3.2.2: Struct Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node {
    #[kfl(property)]
    a: i32,
    #[kfl(properties)]
    b: HashMap<String, i32>
}
}

Case 4: Mix of Arguments and Properties

Case 5: Child

node {
  child 1
}

Pattern 5.: Tuple Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node(#[kfl(child)] A);

// Or Struct Struct
#[derive(Decode)]
struct A(#[kfl(argument)] i32);
}

Pattern 5.: Struct Struct

#![allow(unused)]
fn main() {
#[derive(Decode)]
struct Node {
    #[kfl(child)]
    child: A
}

// Or Struct Struct
#[derive(Decode)]
struct A(#[kfl(argument)] i32);
}

NOTE: A is redundant.

Case ?: Children

Case ?: Alteration

node0 1
node1 "hoge"
#![allow(unused)]
fn main() {
#[derive(Decode)]
enum Node {
    Node0(#[kfl(argument)] i32),
    Node1(#[kfl(argument)] String)
}
}
// #[derive(Debug, kfl::Decode)]
// struct Document {
//     #[kfl(children(name = "plugin"))]
//     plugins: Vec<String>,
//     #[kfl(children(name = "file"))]
//     files: Vec<String>,
// }

// #[derive(Debug, kfl::Decode)]
// struct Node {
//     #[kfl(property)] a: String,
//     #[kfl(property)] b: String,
//     // #[kfl(child)] c: Child
// }

// #[derive(Debug, kfl::Decode)]
// struct Node {
//     #[kfl(argument)] a: String,
//     #[kfl(argument)] b: String,
//     #[kfl(child)] c: Child
// }

// #[derive(Debug, kfl::Decode)]
// struct Child(
//     #[kfl(argument)] String,
//     #[kfl(argument)] String
// );

fn main() {
    // let doc: Document = kfl::parse("test", &document).unwrap();
    let doc: Vec<Node> = kfl::parse("test", &document).unwrap();
    println!("{:?}", &doc);
}

Rationale

DecodePartial

Default + DecodePartial vs FromIterator

Appendix

knuffel

Misconceptions

  • Children
    • Not eble to use enum as a single child
  • Nominal
    • Flatten properties (analogous to serde)
      • Not implemented DecodeScalar for structs
    • NodeName
    • TypeName
      • hence type annotations for nodes and scalars are different
    • bool child
  • Option vs Default
    • benefit: form of type path doesn't matter
  • unwrap
  • NewType for structs and Nested for enums
    • Every data in KDL appears either as a scalar wrapped in a node or a node itself. Therefore every type can exsist only in an already newtypeed form.
  • str and bytes → simply implement DecodeScalar for SocketAddr and Vec<u8>