Parse integer or provide default value with unwrap_or, but print error message when default value is used - rust

I have written some code that parses a config file. If the config file holds a valid value for a field it sets in Config struct. If no valid integer value was found for a setting it sets a default value (e.g: 90).
let config = Config {
interval: settings.get("interval").unwrap().parse().unwrap_or(90),
}
How can I make this to take a closure, so that it can print via error! and set default value?
Looking something like following:
let config = Config {
interval: settings.get("interval").unwrap().parse().unwrap_or({
error!("No interval found. Using default: 90");
90
});
}
But in this example, the error! is always executed, even if a valid value from interval was read from the config.
How can I make it so that unwrap_or only executes the code in optb when parse() has failed?

How can I make it so that unwrap_or only executes the code in optb when parse() has failed?
Arguments passed to unwrap_or are eagerly evaluated.
If you are passing the result of a function call, it is recommended
to use unwrap_or_else which is lazily evaluated.
In your scenario it should be changed as following:
let config: Config = Config {
interval: settings.get("interval").unwrap().parse().unwrap_or_else(|_| {
error!("No interval found. Using default: 90");
90
}),
}
Playground
Also you should not use naked unwrap() in your production code.
Error handling is better solution instead of using naked unwrap()
Here you can find detailed info about why you should not use unwrap()

Related

Jest Expected and receive are identical

Is there a reason why Jest would see this as a not identical when both seem exactly the same?
So here is the code I am using to do the test, it's basically just a function that calls for an event emiter, in the event emiter if the date is invalid, I let it as is :
const datepickerComponent: Datepicker = new Datepicker();
const mockEvent = {
target: {
classList: {
remove: jest.fn(),
add: jest.fn(),
},
value: '01-01-197',
},
} as unknown as InputEvent;
datepickerComponent.onInput(mockEvent);
const emitMock: jest.Mock = jest.fn();
datepickerComponent.dsdDatepickerInputChange = { emit: emitMock } as unknown as EventEmitter<dsdDatepickerInputChangeEvent>;
// when
datepickerComponent.onInput(mockEvent);
const dateValue = new Date('197-01-01T00:00:00');
// then
expect(emitMock).toHaveBeenCalledWith({ value: '197-01-01', valueAsDate: dateValue });
The reason why you are observing this error is because whilst Date { NaN } values look the same, they actually refer to different object instances and cannot be traversed for equality any further, hence the actual error should be the following:
Expected: {"value": "197-01-01", "valueAsDate": Date { NaN }}
Received: serializes to the same string
(To reproduce this error - create two new dates using new Date('197-01-01T00:00:00') and pass them into .equals())
To get past this error, all you need to do is to simply refactor your .toHaveBeenCalledWith test into the following:
const calledWithArg = emitMock.mock.calls[0][0];
expect(JSON.stringify(calledWithArg)).toEqual(JSON.stringify({ value: '197-01-01', valueAsDate: dateValue }));
The reason why .toHaveBeenCalledWith does not work is because it does not allow us to reshape the argument object before a comparison (in our case we need to stringify it), hence we can alternatively extract the argument that the mock was called with via .mock.calls[0][0], stringify it and then compare it to the stringified version of the expected object.
Reference of valueAsDate is different. But Jest doc affirm it uses .toEqual for comparison. That means it try to deeply compare two new Date. This is a bad idea, you don't control the Date class, and there are probably some moving parts inside.
To loose match a value, you can check this issue: https://stackoverflow.com/a/55569458/7696155

Enum attribute in lit/lit-element

We are trying to build a component with a property variant that should only be set to "primary" or "secondary" (enum). Currently, we are just declaring the attribute as a String, but we were wondering if there is a better way for handling enums? For example, should we validate somehow that the current value is part of the enum? Should we throw an error if not?
I asked this question on Slack and the answers I got lean towards declaring the property as String and use hasChanged() to display a warning in the console if the property value is invalid.
Standard HTML elements accept any string as attribute values and don't throw exceptions, so web components should probably behave the same way.
This all sounds reasonable to me.
If you're using TypeScript I'd recommend just using strings. You can use export type MyEnum = 'primary' | 'secondary' to declare it and then use #property() fooBar: MyEnum to get build time checking. You can use #ts-check to do this in plain JS with #type MyEnum too.
This works well if the enums are for component options or that map to server-side enums that will get validated again.
However, if you want to validate user input into enums or loop through them a lot this is less good. As the JS runs it has no visibility of the type. You need an object dictionary, something like:
const MyEnum = Object.freeze({
primary: 'primary',
secondary: 'secondary'
});
// Enforce type in TS
const value: keyof MyEnum;
// Validate
const validated = MyEnum[input.toLower()];
// Loop
for(const enumVal of Object.keys(MyEnum)) ...
// Or Convert to a different value type
const MyEnum = Object.freeze({
primary: 1,
secondary: 2
});
These are somewhat idiosyncratic. Again, if you're using TypeScript it has an enum keyword that compiles to something like this and I'd use that rather than rolling your own. Strings are the better option unless you need to validate, loop or convert the values.

Terraform variable to assign using function

variable "cidr" {
type = map(string)
default = {
development = "x.1.0.0/16"
qa = "x.1.0.0/16"
default = "x.1.0.0/16"
}
}
variable "network_address_space" {
default = lookup(var.cidr, var.environment_name,"default")
}
Am getting error that "Error: Function calls not allowed"
variable "subnet_address_space": cidr_subnet2_address_space = cidrsubnet(var.network_address_space,8,1)
A Terraform Input Variable is analogous to a function argument in a general-purpose programming language: its value comes from an expression in the calling module, not from the current module.
The default mechanism allows us to substitute a value for when the caller doesn't specify one, but because variables are intended for getting data into a module from the outside, it doesn't make sense to set the default to something from inside that module: that would cause the result to potentially be something the caller of the module could never actually specify, because they don't have access to the necessary data.
Terraform has another concept Local Values which are roughly analogous to a local variable within a function in a general-purpose programming language. These can draw from function results and other objects in the current module to produce their value, and so we can use input variables and local values together to provide fallback behaviors like you've illustrated in your question:
var "environment_name" {
type = string
}
var "environment_default_cidr_blocks" {
type = map(string)
default = {
development = "10.1.0.0/16"
qa = "10.2.0.0/16"
}
}
var "override_network_range" {
type = string
default = null # If not set by caller, will be null
}
locals {
subnet_cidr_block = (
var.override_network_range != null ?
var.override_network_range :
var.environment_default_cidr_blocks[var.environment_name]
)
}
Elsewhere in the module you can use local.subnet_cidr_block to refer to the final CIDR block selection, regardless of whether it was set explicitly by the caller or by lookup into the table of defaults.
When a module uses computation to make a decision like this, it is sometimes useful for the module to export its result as an Output Value so that the calling module can make use of it too, similar to how Terraform resources also export additional attributes recording decisions made by the provider or by the remote API:
output "subnet_cidr_block" {
value = local.subnet_cidr_block
}
As stated in Interpolate variables inside .tfvars to define another variable by the Hashicorp person, it is intended to be constant by design.
Input variables are constant values passed into the root module, and so they cannot contain interpolations or other expressions that do not yield a constant value.
We cannot use variables in backend either as in Using variables in terraform backend config block.
These are the things we Terraform users tripped on at some point, I suppose.

Node.js global variables not working

I want to use global variables in my project, but they don't work and I don't understand why they don't work. I'm trying to use them in the following way:
global.connection=null;
function create_connection(connection) {
connection=12345;
}
create_connection(global.connection);
console.log(global.connection); // returns null, why doesn't it return 12345?
Javascript always passes variables by value. So in your case you change the string value without keeping reference to global object.
You could have done this instead
function create_connection(global) {
global.connection=12345;
}
create_connection(global);

Applying default groovy method parameter value when passing null

In Groovy, if I have:
def say(msg = 'Hello', name = 'world') {
"$msg $name!"
}
And then call:
say() // Hello world!
say("hi") // Hi world!
say(null) // null world!
Why is the last one getting interpreted literally as null and not applying the default value? Doesn't this defeat the purpose of default method argument values? I do get that passing null is different from not passing anything w/r/t argument length.
My problem here is that if I now have a method that takes a collection as an argument:
def items(Set<String> items = []) {
new HashSet<>(items)
}
This will throw a NullPointerException if I call items(null) but work fine if I just say items(). In order for this to work right, I have to change the line to be new HashSet<>(items ?: []) which, again, seems to defeat the entire purpose of having default method argument values.
What am I missing here?
In Groovy, default parameters generates overloaded methods. Thus, this:
def items(Set<String> items = []) {
new HashSet<>(items)
}
Will generate these two methods (I used javap to get these values):
public java.lang.Object items(java.util.Set<java.lang.String>);
public java.lang.Object items();
So when you call items(null) you are, in fact, passing some value, and items(Set) method will be used.
You can also refer to this question about default parameters.

Resources