doc_code_snippets! {
mod "guide_11",
type_ident=Guide11,
template=r##"
This chapter demonstrates creating a `type module`,
which is a way to pass many types around with just 1 generic parameter,by using a ConstValue.
Say that we want to read some type from csv file,in this case a Point2D.
//@use_codeblock:point_struct,ignore
This is the definition of Point2D,which we are parsing from the file.
//@use_codeblock:point_from_str,ignore
This is how we parse Point2D from a string.
//@use_codeblock:module_struct,ignore
This is the `type module`,used to pass multiple types within a single type parameter.
These are the types in `ConstModule`:
- `type_`:
is the type being parsed.
- `collection`:
is the collection we are collecting into.
- `get_default`:
is closure type which returns the default value in case that parsing failed.
The `bound` attribute on every field gets passed to the associated types in `ModuleTrait`.
These bounds are then enforced in `impl ModuleTrait for ConstModule< ... >`.
//@use_codeblock:new_module,ignore
This constructs a ConstModule,
requiring the caller to pass the types for
the type being parsed and the collection it will be stored into.
This function uses the `Construct` type alias in its return type to ensure that it
initializes every ConstModule field.
Examples of calling this function:
- `new_module(u32::T ,Vec::T)`
- `new_module(::T ,Vec::::T)`
- `new_module(0u8.ty_(),>::T)`
- `new_module(String::T, BTreeSet::T )`
//@use_codeblock:start_lines,ignore
This function takes the multiline string `lines` ,
creates a lines iterator,
then parses each line as an `M::type_`,
using `get_default` to get the default value for that type if parsing fails,
and finally collects all parsed elements into an `M::collection`.
//@use_codeblock:main,ignore
Here we start by instantiating a ConstModule with
`type_=Point2D` and `collection=BTreeSet`.
Then we parse the text using that module,
providing the closure which returns the default value for each line that couldn't be parsed.
Then we declare a BTreeSet ,
to check that the return value of calling parse_lines is what we expect.
# The entire thing
//@use_codeblock:all,rust
"##,
code=r##"
//@codeblock-start:all
#[macro_use]
extern crate type_level_values;
#[macro_use]
extern crate derive_type_level;
use std::collections::BTreeSet;
use std::fmt;
use std::iter::FromIterator;
use std::num::ParseIntError;
use std::str::FromStr;
use type_level_values::field_traits::*;
use type_level_values::prelude::*;
//@codeblock-start:point_struct
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
pub struct Point2D {
pub x: u32,
pub y: u32,
}
//@codeblock-end :point_struct
//@codeblock-start:point_from_str
impl FromStr for Point2D {
type Err = InvalidPoint2DStr;
fn from_str(s: &str) -> Result {
let mut ints=s.split(",").map(u32::from_str);
let x=ints.next().ok_or_else(|| {
InvalidPoint2DStr::InvalidString(s.into())
})??;
let y=ints.next().ok_or_else(||{
InvalidPoint2DStr::InvalidString(s.into())
})??;
Ok(Point2D { x, y })
}
}
#[derive(Debug)]
pub enum InvalidPoint2DStr {
InvalidInteger(ParseIntError),
InvalidString(String),
}
impl From for InvalidPoint2DStr{
fn from(e:ParseIntError)->Self{
InvalidPoint2DStr::InvalidInteger(e)
}
}
//@codeblock-end :point_from_str
//@codeblock-start:module_struct
#[derive(TypeLevel)]
#[typelevel(reexport(Struct, Traits))]
#[allow(dead_code)]
pub struct Module {
#[typelevel(bound = "FromStr+fmt::Debug")]
pub type_: (),
#[typelevel(bound = "FromIterator+Default+Extend")]
pub collection: (),
#[typelevel(bound = "FnMut(::Err)->Self::type_")]
pub get_default: (),
}
use self::type_level_Module::fields as module_type;
//@codeblock-end:module_struct
//@codeblock-start:new_module
pub fn new_module(
_type: VariantPhantom,
_collection: VariantPhantom,
) ->
Construct
{
ConstModule::MTVAL
}
//@codeblock-end:new_module
//@codeblock-start:start_lines
/// Parses every line of `lines` as a `M::type_` and collects them into a `M::collection`.
pub fn parse_lines(
lines: &str,
_module: M,
mut get_default: M::get_default,
) -> M::collection {
lines
.lines()
.map(|s| s.parse::().unwrap_or_else(&mut get_default))
.collect()
}
//@codeblock-end :start_lines
//@codeblock-start:main
fn main() {
let module = new_module(Point2D::T, BTreeSet::T);
let text="\
10,20\n\
asdasd\n\
20,40\
";
let value = parse_lines(text, module, |_| Point2D { x: 0, y: 100 });
let set = BTreeSet::new().mutated(|v| {
v.insert(Point2D { x: 10, y: 20 });
v.insert(Point2D { x: 0, y: 100 });
v.insert(Point2D { x: 20, y: 40 });
});
assert_eq!(value, set);
}
//@codeblock-end :main
"##,
}