How to use metaprogramming to create an enum in Perl 6? - metaprogramming

Given data that represents an enum, such as:
my %enums := {
Color => { red => 0, black => 1, green => 2 },
Status => { fail => 0, pass => 1 }
};
How can I use Metamodel::ClassHOW to create enums equivalent to:
enum Color ( red => 0, black => 1, green => 2 );
enum Status ( fail => 0, pass => 1 );
Timo's ADT library gives an example of how to create a class with ClassHOW, but it doesn't cover enums: https://github.com/timo/ADT/blob/master/lib/ADT.pm6

This seems to do the trick, but it's mostly untested:
my %enums := {
Color => { red => 0, black => 1, green => 2 },
Status => { fail => 0, pass => 1 }
};
my #types = gather {
for %enums.kv -> $name, %values {
my $type = Metamodel::EnumHOW.new_type(:$name, base_type => Int);
for %values -> $pair {
$type.^add_enum_value($pair);
}
$type.^add_role(Enumeration);
$type.^add_role(NumericEnumeration);
$type.^compose;
take $type;
}
}.list;
say #types; # Output: [(Status) (Color)]
Note that this puts the types into a data structure, because lexical scopes are immutable at run time, so you can't declare them just as you would with enum Color ....

Related

Matching on enum : First arm unexpectedly always matching

I have a wierdest case yet on my hands. I have an enum that I convert to a string. The enum provided is eg. Green so the string returned from the match is "text-success". Simple right? Turns out that the string returned is always "" regardless of how I obtain it. This makes no sense to me. Please help!
fn bootstrap_table_color (e: Color) -> String {
let s: String = match &e {
White => "".to_string(),
Blue => String::from("table-info"),
Green => "table-success".to_string(),
Yellow => "table-warning".to_string(),
Red => "table-danger".to_string(),
};
println!("bootstrap_table_color ({:?}) -> {:?}", e, s);
return s;
}
bootstrap_table_color (Blue) -> ""
bootstrap_table_color (Green) -> ""
That's because all possible values match the White variable arm.
You could see it by doing
let s: String = match &e {
White => format!("White={:?}", White),
}
The clean solution is to prefix arm values with your enum name:
let s = match &e {
Color::White => "".to_string(),
Color::Blue => String::from("table-info"),
Color::Green => "table-success".to_string(),
Color::Yellow => "table-warning".to_string(),
Color::Red => "table-danger".to_string(),
};
Another solution would be to do a use Color::*; but you would be vulnerable to typos or changes.

Where will String::from("") be allocated in a match arm?

I am still very new to rust, coming from a C embedded world.
If i have a piece of code like this:
match self {
Command::AT => String::from("AT"),
Command::GetManufacturerId => String::from("AT+CGMI"),
Command::GetModelId => String::from("AT+CGMM"),
Command::GetFWVersion => String::from("AT+CGMR"),
Command::GetSerialNum => String::from("AT+CGSN"),
Command::GetId => String::from("ATI9"),
Command::SetGreetingText { ref enable, ref text } => {
if *enable {
if text.len() > 49 {
// TODO: Error!
}
write!(buffer, "AT+CSGT={},{}", *enable as u8, text).unwrap();
} else {
write!(buffer, "AT+CSGT={}", *enable as u8).unwrap();
}
buffer
},
Command::GetGreetingText => String::from("AT+CSGT?"),
Command::Store => String::from("AT&W0"),
Command::ResetDefault => String::from("ATZ0"),
Command::ResetFactory => String::from("AT+UFACTORY"),
Command::SetDTR { ref value } => {
write!(buffer, "AT&D{}", *value as u8).unwrap();
buffer
},
Command::SetDSR { ref value } => {
write!(buffer, "AT&S{}", *value as u8).unwrap();
buffer
},
Command::SetEcho { ref enable } => {
write!(buffer, "ATE{}", *enable as u8).unwrap();
buffer
},
Command::GetEcho => String::from("ATE?"),
Command::SetEscape { ref esc_char } => {
write!(buffer, "ATS2={}", esc_char).unwrap();
buffer
},
Command::GetEscape => String::from("ATS2?"),
Command::SetTermination { ref line_term } => {
write!(buffer, "ATS3={}", line_term).unwrap();
buffer
}
}
How does it work in Rust? Will all these match arms evaluate immediately, or will only the one matching create a mutable copy on the stack? And also, will all the string literals withing my String::from("") be allocated in .rodata?
Is there a better way of doing what i am trying to do here? Essentially i want to return a string literal, with replaced parameters (the write! macro bits)?
Best regards
Only the matching arm will be evaluated. The non matching arms have no cost apart the size of the program.
In the general case, it's not even possible to evaluate other arms, as they depend on data read using destructuring of the pattern.
As for your second question, the location in a program where literals are stored isn't commonly named rodata, and it's neither specified nor guaranteed (it's usually deduplicated but that's just optimization).

Conditional Split when Array for XML input Logstash

I am trying to parse a XML file and want to split. But the split field is not always array.
XML structure:
<A a="1">
<B b="1" q="10">
<C c="1" r="20"></C>
</B>
<B b="2" q="11">
<C c="2" r="21"></C>
<C c="3" r="22">
<M m="1" />
</C>
<B>
</A>
When I try to split as
xml {
source => "message"
target => "doc"
force_array => false
}
split {
field => "doc[B][C]"
}
I get error as Split can be done only in strings or arrays.
How can I mutate the Strings into Array or is there another way to solve this?
Expected output:
{
C => {
c => 1,
r => 20,
}
B => {
b => 1,
q => 10,
}
}
{
C => {
c => 2,
r => 20,
}
B => {
b => 2,
q => 11,
}
}
{
C => {
c => 3,
r => 22,
M => {
m => 1
}
}
B => {
b => 2,
q => 11,
}
}
Each nested acts as a separate event and stored as a different event (row) in elastic search.

How to create multilingual menu link programmatically in Drupal 7

I'm trying to create the menu link programmatically. But its not working where source language is other than english. Here is my code.
$language_list = language_list();
foreach ($language_list as $language_code => $language_object) {
$menu_item = array(
'link_title' => t('Fruit'),
'menu_name' => 'menu-main-footer',
'customized' => 1,
'link_path' => $custom_path,
'language' => $language_code,
'weight' => 30,
);
menu_link_save($menu_item);
}
Any one have some idea on this?
I changed my code. And it work for me.
// Create menu translation set.
$menu_translation_set = i18n_translation_set_create('menu_link');
// Create translated menu link for all site enable language.
$language_list = language_list();
foreach ($language_list as $language_code => $language_object) {
// Add Fruit link in menu-main-footer.
// 'change-fruit' is node title.
$fruit_path = drupal_get_normal_path('change-fruit', $language_code);
if (!menu_link_get_preferred($fruit_path, 'menu-main-footer')) {
$menu_item = array(
'link_title' => t('fruit'),
'menu_name' => 'menu-main-footer',
'customized' => 1,
'link_path' => $fruit_path,
'language' => $language_code,
'weight' => 30,
'i18n_tsid' => $menu_translation_set->tsid,
);
menu_link_save($menu_item);
$menu_translation_set->add_item($menu_item, $language_code);
$menu_translation_set->save();
}
}
May be helpful to other.
I had to migrate an old menu to a new one with its localized translations so here is what I did :
$old_name = 'menu-old';
$new_name = 'menu-new';
$old_menu = menu_load($old_name);
if(isset($old_menu)){
$old_mlids = db_query("SELECT mlid from {menu_links} WHERE menu_name=:menu_name", array(':menu_name' => $old_name))->fetchAll();
if(!empty($old_mlids)){
// Clean existing items in new menu.
$new_mlids = db_query("SELECT mlid from {menu_links} WHERE menu_name=:menu_name", array(':menu_name' => $new_name))->fetchAll();
if(!empty($new_mlids)){
foreach($new_mlids as $record){
menu_link_delete($record->mlid);
}
}
// Copy old to new menu.
foreach($old_mlids as $record){
$old_menu_item = menu_link_load($record->mlid);
$new_menu_item_config = array(
'link_title' => $old_menu_item['link_title'],
'link_path' => $old_menu_item['link_path'],
'menu_name' => $new_name,
'customized' => 1,
'weight' => $old_menu_item['weight'],
'expanded' => $old_menu_item['expanded'],
'options' => $old_menu_item['options'],
);
$new_menu_item = $new_menu_item_config;
menu_link_save($new_menu_item);
// Migrate translations.
$languages = language_list('enabled')[1];
foreach($languages as $lang_code => $language_object){
if ($lang_code == language_default('language')) {
continue;
}
$translation_value = i18n_string_translate('menu:item:'.$old_menu_item['mlid'].':title', $old_menu_item['link_title'], array('langcode' => $lang_code));
if($translation_value != $old_menu_item['link_title']){
i18n_string_translation_update('menu:item:'.$new_menu_item['mlid'].':title', $translation_value, $lang_code, $old_menu_item['link_title']);
}
}
}
}
// Delete old menu.
menu_delete(array('menu_name' => $old_name));
}

Create a file for each defined block with puppet

I have a running manifest, where I create a folder and a file from a setting (exerpt):
define ffnord::mesh(
$mesh_if_id = "low",
$mesh_mtu_low = 1280,
$fastd_low_port = 11280, # fastd port
) {
ffnord::fastd { "fastd_${mesh_code}":
mesh_if_id => $mesh_if_id,
mesh_mtu_low => $mesh_mtu_low,
fastd_low_port => $fastd_low_port,
}
}
and
define ffnord::fastd( $mesh_if_id
, $mesh_code
, $mesh_mtu_low = 1280
, $fastd_low_port
) {
file {
"/etc/fastd/${mesh_code}-mesh-low-vpn/":
ensure =>directory,
require => Package[ffnord::resources::fastd];
"/etc/fastd/${mesh_code}-mesh-low-vpn/fastd.conf":
ensure => file,
notify => Service[ffnord::resources::fastd],
content => template('ffnord/etc/fastd/fastd-low.conf.erb');
}
}
How can I define a variable amount of those configs:
$mesh_if_id = "low",
$mesh_mtu_low = 1280,
$fastd_low_port = 11280, # fastd port
$mesh_if_id = "something",
$mesh_mtu_low = 12345,
$fastd_low_port = 112345, # fastd port
...
and loop through those blocks to create a folder and file inside ffnord/etc/fastd/ for each block automatically?
(I want to solve this problem: https://github.com/ffnord/ffnord-puppet-gateway/pull/116#issuecomment-100619610 )
In Puppet 3.x there is no "looping", but there are a few tricks. You can pass a Hash of data that represents N number of ffnord::fastd instances:
define define ffnord::mesh($fastd_hash) {
create_resources('ffnord::fastd', $fastd_hash)
}
define ffnord::fastd($mesh_code, $fastd_low_port, $mesh_mtu_low = 1280) {
file {
"/etc/fastd/${mesh_code}-mesh-low-vpn/":
ensure =>directory,
require => Package[ffnord::resources::fastd];
"/etc/fastd/${mesh_code}-mesh-low-vpn/fastd.conf":
ensure => file,
notify => Service[ffnord::resources::fastd],
content => template('ffnord/etc/fastd/fastd-low.conf.erb');
}
}
$hash_of_fastds = {
"low_id" => {
mesh_code => 'low,
mesh_mtu_low => 1280,
fastd_low_port => 11280,
},
"some_id" => {
mesh_code => 'something',
mesh_mtu_low => 12345,
fastd_low_port => 112345,
},
}
ffnord::mesh { 'foo': fastd_hash => $hash_of_fastds, }
Note I've modified define ffnord::fastd slightly, where you had a $mesh_if_id parameter I've turned this into the $namevar of ffnord::fastd.
The first level of $hash_of_fastds translates to the names of the ffnord::fastd instances, the second level of the hash are the parameters for each ffnord::fastd.
See the documentation on the create_resources function for more information.
In Puppet 4 you could use the each function to achieve a similar result.

Resources