diff --git a/www/Caddyfile b/www/Caddyfile index c503c05..691cadd 100644 --- a/www/Caddyfile +++ b/www/Caddyfile @@ -1,19 +1,7 @@ +jeffa.io, +www.jeffa.io, localhost:8080 { - redir http://www.{host}:{port}{uri} -} -jeffa.io { - redir https://www.{host}{uri} -} - -www.localhost:8080, -www.jeffa.io { root * /srv/ - file_server { - index /srv/index.html - } - rewrite @not_html {path}.html - @not_html { - not path *.html - } + file_server + try_files {path} {path}.html {path}/ 404 } - diff --git a/www/Containerfile b/www/Containerfile index 7048c34..07a5435 100644 --- a/www/Containerfile +++ b/www/Containerfile @@ -1,13 +1,15 @@ FROM alpine as build -RUN apk update && apk add pandoc +RUN apk update && apk add pandoc fish -WORKDIR /jeffa.io/ +WORKDIR /www.jeffa.io/ COPY . . +RUN fish build.fish + FROM caddy:2-alpine COPY Caddyfile /etc/caddy/Caddyfile -COPY --from=build /jeffa.io/out /srv +COPY --from=build /www.jeffa.io/out /srv diff --git a/www/build.fish b/www/build.fish index cf16342..4ac8864 100644 --- a/www/build.fish +++ b/www/build.fish @@ -1,13 +1,19 @@ +if ! test -d out/ + mkdir out/ +end + +cp basic.css out/index.css + for file in src/**/*.md set -l output (string replace .md .html $file) set -l output (string replace src/ out/ $output) + pandoc \ --to html \ - --css basic.css \ - --self-contained \ + --embed-resources \ + --standalone \ + --template templates/page.html \ + --email-obfuscation references \ $file \ > $output end - -podman build -t www.jeffa.io . -podman run -p 8080:8080 www.jeffa.io:latest diff --git a/www/run.fish b/www/run.fish new file mode 100644 index 0000000..ba86dd9 --- /dev/null +++ b/www/run.fish @@ -0,0 +1,2 @@ +podman build -t www.jeffa.io . +podman run -p 8080:8080 www.jeffa.io:latest diff --git a/www/server.bu b/www/server.bu index 95535be..f8b0aff 100644 --- a/www/server.bu +++ b/www/server.bu @@ -2,14 +2,14 @@ variant: fcos version: 1.5.0 systemd: units: - - name: www_jeffa_io.service + - name: hello.service enabled: true contents: | [Unit] - Description=caddy run + Description=Run jeffa.io [Service] Type=oneshot - RemainAfterExit=yes - ExecStart=podman-compose up + RemainAfterExit=no + ExecStart=/usr/bin/podman run git.jeffa.io/jeff/www.jeffa.io [Install] - WantedBy=multi-user.target + WantedBy=multi-user.target \ No newline at end of file diff --git a/www/src/an_auto_increment_crate_for_rust.md b/www/src/an_auto_increment_crate_for_rust.md new file mode 100644 index 0000000..b9118ac --- /dev/null +++ b/www/src/an_auto_increment_crate_for_rust.md @@ -0,0 +1,84 @@ +--- +date: Jan 26th, 2021 +--- + +# An Auto-Increment Crate for Rust + +...now exists! [Check it out.][1] + +## The Problem + +I was recently working on a Rust program and needed a way to give instances a unique identifier. These IDs did not need to be *universally* unique. I wanted the simplest way to recognize two instances as unique even if they were identical aside from the ID. A serial (or auto-increment) ID seemed like an appropriate choice. + +As a rule, I try to write my own code to avoid over-reliance on dependencies. But this case seemed to warrant a dependency. It would be a simple thing to implement. What I wanted was essentially a counter, after all. I expected to find a reliable crate with lots of downloads. + +Surprisingly, no such crate existed. I tried searching for "serial", "increment", "auto increment", "serial id", etc. and found nothing. One [crate][2] had a promising name but no documentation and, from what I could tell from the code, did not quite do what I was looking for. + +I decided to write and publish such a crate myself. It seemed like a good opportunity to contribute a small but useful crate with relatively simple code to the greater Rust ecosystem. + +## The Solution + +### Are Universally Unique IDs a Universal Solution? + +Serial IDs are used as identifiers because they are simple. Universally unique identifiers (or UUIDs), on the other hand, are usually used when a program needs to create identifiers without access to information about IDs created elsewhere. This is why a distributed database would use them. Rust developers have access to the [rand] crate and the [uuid] crate, which uses rand internally but creates IDs in formats that conform to formal standards. The basic strategy is to create a pseudo-random value or to capture a value (such as a UTC timestamp) and hash it. The details of UUID generation are too extensive to get into here. The uuid crate is both well-documented and cleanly coded, and there are countless other descriptions of the standards it implements. + +The important difference between serial IDs and UUIDs is the performance cost. Hashing and random number generation are more expensive than increasing the value of an integer by one. Even if threads have to wait to get exclusive access to an ID generator, each thread will only hold it for the number of nanoseconds that it takes to calculate "x + 1". Serial IDs are also free from the issue of collisions, allowing them to be smaller. A 32-bit serial ID can have 4,294,967,296 unique instances. There is no probability to consider. + +*UUIDs are always good enough, but their cost is not always necessary.* Git, for example, creates an SHA-1 has of a commit's content to create a commit ID. The IDs need to be unique when commits from different machines are pushed to a single remote. But many IDs do not need a high probability of being universally unique. CLI applications or other client-side programs that only work with in-memory values or store data locally are such examples. + +### How Has This Been Implemented Before? + +Serial IDs are familiar to users of database systems. In SQL, auto-increment integers can be used by setting `GENERATE BY DEFAULT AS IDENTITY` as the default value for a column. + +```sql +CREATE TABLE distributors ( + did integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + name varchar(40) NOT NULL CHECK (name <> '') +); +``` + +[Source][Postgres: CREATE TABLE] + +Postgres has its own `SERIAL` type that mimics this behavior. + +```sql +CREATE TABLE tablename ( + colname SERIAL +); +``` + +[Source][Postgres: SERIAL] + +Postgres provides a reasonable standard to replicate: 32-bit integers by default with support for any other unsigned integer. By using a trait to define the types that can be generated, users can make their own types compatible with the API provided by the crate. My crate just needs to provide the generator, the trait and implementations for unsigned integers in the standard library. + +## Goals + +- Small: The crate should be tiny. This is just a counter. +- Safe: IDs should be unique to each generator. There should be no panics. +- Compatible: Prefer primitives as the output values instead of a new type. +- Usable: Use feature flags to offer serde implementations, atomic primitives and other features + that may be useful to some users. + +## Takeaway + +This process proved something about Rust that wasn't readily apparent to me before. Encouraging flexibility by defining behavior (through traits) makes it intuitive to implement new features. When you know what you want to do the code starts to seem obvious. If you need a single type that can output many types, define the common behavior of those types and make the single type operate based on that shared behavior. + +Rust is often described as flexible, but many languages are flexible. With Rust, it feels like the API writes itself once you clearly define what it should do. The code is not just expressive, it's obvious. + +I also realized that there are still plenty of opportunities to contribute Rust code. Both widely-applicable and more niche libraries have provided the tools that developers need to adopt the language. But while Rust has become mature enough to fit many use cases, the ecosystem is far from bloated. There is still plenty of space to do new or different things and make an impact. + +The serial_int crate is on [crates.io][1] and [lib.rs][lib.rs]. Contributions and feedback on the API are welcome! + +[1]: https://crates.io/crates/serial_int + +[2]: https://crates.io/crates/increment + +[lib.rs]: https://lib.rs/crates/serial_int + +[Postgres: CREATE TABLE]: https://www.postgresql.org/docs/13/sql-createtable.html + +[Postgres: SERIAL]: https://www.postgresql.org/docs/13/datatype-numeric.html#DATATYPE-SERIAL + +[rand]: https://crates.io/crates/rand + +[uuid]: https://crates.io/crates/uuid diff --git a/www/src/index.md b/www/src/index.md index 6f84933..794ff3e 100644 --- a/www/src/index.md +++ b/www/src/index.md @@ -1,13 +1,13 @@ -# jeffa.io - I am a programmer in New York. This is my website. - Sites - - [jeffa.io](https://jeffa.io) + - [jeffa.io](https://www.jeffa.io) - [git.jeffa.io](https://git.jeffa.io) - [Sourcehut](https://sr.ht/~jeffa) - [Github](https://github.com/solaeus) - Articles - - [It's Time to Rethink the Command Line Shell](its_time_to_rethink_the_command_line_shell.html) + - [It's Time to Rethink the Command Line Shell](its_time_to_rethink_the_command_line_shell) + - [An Auto-Increment Crate for Rust](an_auto_increment_crate_for_rust) + - [Rust Packages vs Crates](rust_packages_vs_crates) diff --git a/www/src/rust_packages_vs_crates.md b/www/src/rust_packages_vs_crates.md new file mode 100644 index 0000000..776dcd7 --- /dev/null +++ b/www/src/rust_packages_vs_crates.md @@ -0,0 +1,57 @@ +--- +date: Nov 18th, 2021 +--- + +# Rust Packages vs Crates + +It is a common misconception that packages are crates and visa versa. I will +admit that this is a pet peeve. But for Rust coders, it is important to know the +difference because otherwise you are denied an understanding of how Rust code is +organized, shared and consumed. + +## Crate + +A **crate**, like a **module** inside of a crate, is a means of organizing code. + +A **crate** is either a binary or a library. + +A **crate** is not published independently, but rather as a member of its +**package**. + +The compiler knows what a **crate** is and uses **crates** as namespaces for +items. If `std::hash::Hash` is not in scope, you can define your own trait +called `Hash`. + +## Package + +A **package** is a wrapper for at least one crate. + +A **package** is publishable. + +A **package** can contain one or zero library **crates**. + +A **package** can contain any number of binary **crates**. + +When you add a **package** to your dependencies, you consume the one library +**crate** inside of that **package**. + +When you use **cargo run** or **cargo install** without specificying a +**crate**, you consume the one binary **crate** in that **package**. + +When you use **cargo run --bin** or **cargo install --bin** followed by a +**crate** name, you consume the specified binary **crate** in that **package**. + +## Why Is There Confusion About This? + +I think the reason people get mixed up is because [crates.io] is actually a +repository for packages. If you find a library on [crates.io], you add the +*package* to the dependencies in your Cargo.toml file. You don't have to +specificy the crate because a package can only have one library crate. If the +default repository were called "packages.io", this may be less of an issue. + +As always, The Book is there as an easy-to-read authority that clears up these +little confusions. + +[The Rust Programming Language: Packages and Crates](https://doc.rust-lang.org/stable/book/ch07-01-packages-and-crates.html) + +[crates.io]:https://crates.io diff --git a/www/templates/page.html b/www/templates/page.html index d02b622..dd93dc4 100644 --- a/www/templates/page.html +++ b/www/templates/page.html @@ -2,20 +2,22 @@ - - $if(title)$ - $title$ - $endif$ - $if(author)$ - - $endif$ + $if(date)$ - + $endif$ - $styles()$ + + jeffa.io -$body$ +

jeffa.io

+ $body$ +
+