Thursday, July 1, 2021

Missing the ternary operator in Rust

Just out of curiosity, I wrote a Rust macro similar to Visual Basic’s IIf function:

macro_rules! iif {
	($cond:expr, $iftrue:expr, $iffalse:expr) => {
		if $cond { $iftrue } else { $iffalse }
	};
}

Usage:

let n = iif!(1 == 1, "yes", "no");

Close to the ternary operator, which I’m sorely missing in these Rust days.

Wednesday, June 16, 2021

Fixing the horrible Firefox 89 interface

In searching for a fix to the Firefox 89 Proton layout catastrophe, I found a very neat site which tries to summarize all the fixes that can be made in “userChrome.css” file, which can be found in about:profiles internal URL, and enabled with toolkit.legacyUserProfileCustomizations.stylesheets in about:config.

Unfortunately, it’s not possible to restore the menu icons, as of yet, but it’s still a relief from that disaster.

This is my hack for Linux:

/*** Tighten up drop-down/context/popup menu spacing ***/

menupopup > menuitem, menupopup > menu {
	padding-block: 4px !important;
}
:root {
	--arrowpanel-menuitem-padding: 4px 8px !important;
}

/*** Proton Tabs Tweaks ***/

/* Adjust tab corner shape, optionally remove space below tabs */

#tabbrowser-tabs {
	--user-tab-rounding: 0px;
}
@media (-moz-proton) {
	.tab-background {
		border-radius: var(--user-tab-rounding) var(--user-tab-rounding) 0px 0px !important;
		margin-block: 1px 0 !important;
	}
	#scrollbutton-up, #scrollbutton-down { /* 6/10/2021 */
		border-top-width: 1px !important;
		border-bottom-width: 0 !important;
	}
	/* Container color bar visibility */
	.tabbrowser-tab[usercontextid] > .tab-stack > .tab-background > .tab-context-line {
		margin: 0px max(calc(var(--user-tab-rounding) - 3px), 0px) !important;
	}
}

/* Inactive tabs: Separator line style */

@media (-moz-proton) {
	.tabbrowser-tab:not([selected=true]):not([multiselected=true]):not([beforeselected-visible="true"]) .tab-background {
		border-right: 1px solid rgba(0, 0, 0, .20) !important;
	}
	/* For dark backgrounds */
	[brighttext="true"] .tabbrowser-tab:not([selected=true]):not([multiselected=true]):not([beforeselected-visible="true"]) .tab-background {
		border-right: 1px solid var(--lwt-selected-tab-background-color, rgba(255, 255, 255, .20)) !important;
	}
	.tabbrowser-tab:not([selected=true]):not([multiselected=true]) .tab-background {
		border-radius: 0 !important;
	}
	/* Remove padding between tabs */
	.tabbrowser-tab {
		padding-left: 0 !important;
		padding-right: 0 !important;
	}
}

Wednesday, June 2, 2021

Go’s defer in Rust

One of my all-time favorite features of Go is the defer mechanism. It’s so incredibly simple, and so incredibly ingenious and useful. The fact the deferred statements are called during a panic unwind makes me want to use it in other languages, like Rust.

As for Rust, I’m aware of the scopeguard crate, but it seems to be an overkill to such a simple concept. Plus, it does some fishy workaround to make the FnOnce.

So I decided to write my own Rust version of the defer mechanism, which turns out to be a macro that hides the creation of an object. This object holds a FnOnce which runs when at the end of the scope via Drop trait:

/// Internal struct used by the `defer!` macro.
pub struct Defer<F: FnOnce()> {
	func: Option<F>,
}

impl<F: FnOnce()> Defer<F> {
	pub fn new(func: F) -> Self {
		Self { func: Some(func) }
	}
}

impl<F: FnOnce()> Drop for Defer<F> {
	fn drop(&mut self) {
		self.func.take().map(|f| f());
	}
}

/// Defers the execution of a block until the surrounding scope ends.
macro_rules! defer {
	( $($tt:tt)* ) => {
		let _deferred = crate::defer::Defer::new(|| { $($tt)* });
	};
}

Usage is straightforward:

#[macro_use] mod defer; // considering defer.rs at crate's root

fn main() {
	defer! { println!("hi"); }
}

Edit: I published the code above as a crate: defer-lite.

Saturday, May 29, 2021

Sum types in Go

One thing I always missed in Go was sum types. I’ve seen some discussion before. Although Go doesn’t have this as an explicit, native feature, I found a pattern that suits my needs by defining types and interfaces.

In the example below, I simulate a function that would load application resources, which can be extracted by ID, position or a string identifier. First, we define the interface and the subtypes:

type (
	// Variant type for: ResId, ResPos, ResStr.
	Res interface{ implRes() }

	ResId  uint32
	ResPos uint32
	ResStr string
)

func (ResId)  implRes() {}
func (ResPos) implRes() {}
func (ResStr) implRes() {}

The isRes() function acts like a “tag” for each subtype.

Now, a function that receives the variant type:

func LoadResource(variant Res) {
	switch v := variant.(type) {
	case ResId:
		println("ID", uint32(v))
	case ResPos:
		println("Position", uint32(v))
	case ResStr:
		println("String", string(v))
	default:
		panic("Res does not accept a nil value.")
	}
}

Usage is now trivial:

LoadResource(ResId(2001))
LoadResource(ResPos(4))
LoadResource(ResStr("MY_ICON"))

It’s clean and it works remarkably well. I applied this technique in my Windigo library.

Monday, May 17, 2021

Registering file associations in Windows

I always used FileTypesMan to register the Windows file associations. But after Windows 7 or so, some associations simply refused to work, leaving them wrong. In Windows 10, this problem worsened.

Today, after digging the associations created by Reaper, I figured out one of the correct ways to register a file association – there are other exotic ways too, which I couldn’t figure out yet.

These are the Windows Registry entries that must be created:

HKEY_CLASSES_ROOT\.fuu\
    (Default) REG_SZ Fuu.File
    Content Type REG_SZ text/plain
    PerceivedType REG_SZ text
HKEY_CLASSES_ROOT\Fuu.Type\
    (Default) REG_SZ A fuu file.
    DefaultIcon\
        (Default) REG_SZ C:\Temp\fuu.exe,0
    shell\
        (Default) REG_SZ open64
        open64\
            (Default) REG_SZ Open file in Fuu
            command\
                (Default) REG_SZ "C:\Temp\fuu.exe" "%1"

And these entries map correctly in FileTypesMan, which can be used to further changes.

Note that Content Type and PerceivedType will automatically create “Open” entries mapped to default applications, so you probably won’t want those. In its simplest form, this will work:

HKEY_CLASSES_ROOT\.fuu\
    (Default) REG_SZ Fuu.File
HKEY_CLASSES_ROOT\Fuu.Type\
    (Default) REG_SZ A fuu file.
    DefaultIcon\
        (Default) REG_SZ C:\Temp\fuu.exe,0
    shell\
        open\
            command\
                (Default) REG_SZ "C:\Temp\fuu.exe" "%1"

Thursday, May 13, 2021

The ghastly button focus

During my 20 years or so writing Win32 programs, I always relied on SetFocus to focus a control. However, I always noticed a weird behavior on push buttons regarding the BS_DEFPUSHBUTTON style, which was never right. In fact, I remember one of the past WinLamb incarnations which I implemented some trick involving changing the button style when setting the focus.

Anyway, now I found out how to properly set the focus on a control, and it does not involve SetFocus, but rather the WM_NEXTDLGCTL message:

SendMessage(hParent, WM_NEXTDLGCTL, (WPARAM)hButton, MAKELPARAM(TRUE, 0));

And the push button magically sets its own style correctly.

Wednesday, March 31, 2021

Fixing Cargo authentication on GitHub

Today, while performing tests before publishing the first version of WinSafe, I stumbled across a problem I had with Go a few weeks ago. It was caused because GitHub no longer accepts user/password authentications, so everything must be done via SSH. I solved the problem by adding some lines to my ~/.gitconfig file.

But Cargo still cannot fetch packages.

After a lot of digging, I found a setting that finally worked. It involved adding another filter to ~/.gitconfig, which now looks like this:

# Force SSH instead of HTTP
# https://stackoverflow.com/a/27501039/6923555
[url "ssh://git@github.com/"]
	insteadOf = https://github.com/
	
# But crates.io needs to pass
# https://github.com/rust-lang/cargo/issues/8172#issuecomment-659066173
[url "https://github.com/rust-lang/crates.io-index"]
	insteadOf = https://github.com/rust-lang/crates.io-index

The “crates.io” directory was updated and I could use an external crate, something that will be needed when I publish my own.

As a side note, all downloaded crates are stored in ~/.cargo/registry/ directory, which can be safely wiped out to clean the cache.

Thursday, March 25, 2021

Displaying current Git branch in Bash prompt

I decided to show the current Git branch at my bash prompt whenever I’m at a directory which contains a repository. I found a rather convoluted Bash-esque solution, which I adapted to myself:

gitbranch() {
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ \1/'
}
#export PS1="\u@\h \[\e[32m\]\w \[\e[91m\]\$(gitbranch)\[\e[00m\]$ "
export PS1="\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\[\e[33m\]\$(gitbranch)\[\e[00m\]\$ "

It’s compact and it works as intended.

Thursday, February 18, 2021

Using local dependencies with Go modules

Go 1.16, released this week, deprecated GOPATH. I used it extensively to develop my libs before publishing them, but now I’m forced to convert them to modules, which work with remote repos by default. However, there’s a way to work with local dependencies.

As I found here, we can instruct Go toolchain to search for a local repo, instead of a remote one by using this command:

go mod edit -replace github.com/username/repo=../repo

This changes the go.mod file. Now, to clean up the go.sum file, run:

go mod tidy

After that, you should be able to use a local dependency just like the old GOPATH days.

The downloaded files are still cached, though. To finally clean the entire cache, run:

go clean -cache -modcache

Modules eventually needed by other applications will be downloaded again when due.

Tuesday, January 5, 2021

Downcasting boxed errors in Rust

Today I was trying to write polymorphic error handling in Rust, something achievable with dynamic_cast in C++, and incredibly easy in Go. In Rust, of course, it’s overcomplicated.

After a couple frustrating hours of searching with no success, I stumbled upon the downcast_ref method of the Error trait, which finally allowed me to write what I wanted. Oddly enough, I couldn’t find this solution anywhere.

Note that the String concrete type cannot be retrieved.

Playground link here.

Friday, December 11, 2020

Cleaning up a Git repository

Sometimes, after you mess up too much with your Git repository, it ends up with a couple useless objects, which are no longer referenced anywhere.

I created this alias a while ago, collected from sources I don’t remember anymore, to perform a cleanup in the repository. It’s particularly useful right after a git fetch:

alias gitcleanrepo='git fetch origin --prune && git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc "$@"'

Tuesday, November 24, 2020

Inserting auto IDs in Oracle

When dealing with tables with an auto-incremented ID field, Oracle requires an special syntax if you’re about to insert data manually. You must directly call the name of the sequence of that table, followed by a special field:

INSERT INTO person (id_person, name)
VALUES (sq_id_person.NEXTVAL, 'John')

Note that the name of the sequence is actually arbitrary.