Skip to content

Commit

Permalink
README: Fix some typos and word insertions/deletions (#339)
Browse files Browse the repository at this point in the history
  • Loading branch information
cyrozap authored Oct 2, 2024
1 parent 6b3cb01 commit ab3dc7a
Showing 1 changed file with 4 additions and 4 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ impl Encode for Person {
}
```

That's it! We've just created a new ASN.1 that be encoded and decoded to BER, CER, and DER; and nowhere did we have to check the tag, the length, or whether the string was primitive or constructed encoded. All those nasty encoding rules details are completely abstracted away so your type only has handle how to map to and from ASN.1's data model.
That's it! We've just created a new ASN.1 that can be encoded and decoded to BER, CER, and DER; and nowhere did we have to check the tag, the length, or whether the string was primitive or constructed encoded. All those nasty encoding rules details are completely abstracted away so your type only has handle how to map to and from ASN.1's data model.

With all the actual conversion code isolated to the codec implementations you can know that your model is always safe to use. The API has also been designed to prevent you from making common logic errors that can lead to invalid encoding. For example; if we look back our `Encode` implementation, and what if we forgot to use the encoder we were given in `encode_sequence` and tired to use the parent instead?
With all the actual conversion code isolated to the codec implementations you can know that your model is always safe to use. The API has also been designed to prevent you from making common logic errors that can lead to invalid encoding. For example; if we look back at our `Encode` implementation, what if we forgot to use the encoder we were given in `encode_sequence` and tried to use the parent instead?

```text
error[E0501]: cannot borrow `*encoder` as mutable because previous closure requires unique access
Expand Down Expand Up @@ -167,7 +167,7 @@ error[E0500]: closure requires unique access to `encoder` but it is already borr
Our code fails to compile! Which, in this case is great, there's no chance that our contents will accidentally be encoded in the wrong sequence because we forgot to change the name of a variable. These ownership semantics also mean that an `Encoder` can't accidentally encode the contents of a sequence multiple times in their implementation. Let's see how we can try to take this even further.

### Compile-Safe ASN.1 With Macros
So far we've shown how rasn's API takes steps to be safe and protect from accidentally creating an invalid model. However, it's often hard to cover everything in an imperative API. Something that is important to understand about ASN.1 that isn't obvious in the above examples is that; in ASN.1, all types can be identified by a tag (essentially two numbers e.g. `INTEGER`'s tag is `0, 2`). Field and variant names are not transmitted in most encoding rules, so this tag is also used to identify fields or variants in a `SEQUENCE` or `CHOICE`. This means that every that in a ASN.1 struct or enum every field and variant **must have** a distinct tag for the whole type to be considered valid. For example ; If we changed `age` in `Person` to be a `String` like below it would be invalid ASN.1 even though it compiles and runs correctly, we have to either use a different type or override `age`'s tag to be distinct from `name`'s. When implementing the `AsnType` trait yourself this requirement must checked by manually, however as we'll see you generally won't need to do that.
So far we've shown how rasn's API takes steps to be safe and protect from accidentally creating an invalid model. However, it's often hard to cover everything in an imperative API. Something that is important to understand about ASN.1 that isn't obvious in the above examples is that; in ASN.1, all types can be identified by a tag (essentially two numbers e.g. `INTEGER`'s tag is `0, 2`). Field and variant names are not transmitted in most encoding rules, so this tag is also used to identify fields or variants in a `SEQUENCE` or `CHOICE`. This means that in a ASN.1 struct or enum every field and variant **must have** a distinct tag for the whole type to be considered valid. For example ; If we changed `age` in `Person` to be a `String` like below it would be invalid ASN.1 even though it compiles and runs correctly, we have to either use a different type or override `age`'s tag to be distinct from `name`'s. When implementing the `AsnType` trait yourself this requirement must be checked manually, however as we'll see you generally won't need to do that.

Included with rasn is a set of derive macros that enable you to have your ASN.1 model implementation implemented declaratively. The `Encode` and `Decode` macros will essentially auto-generate the implementations we showed earlier, but the real magic is the `AsnType` derive macro. Thanks to the `static-assertations` crate and recent developments in `const fn`; the `AsnType` derive will not only generate your `AsnType` implementation, it will also generate a check that asserts that every field or variant has a distinct tag at *compile-time*. This means now if for some reason we made a change to one of the types in person, we don't have re-check that our model is still valid, the compiler takes care of that for us.

Expand All @@ -193,7 +193,7 @@ s are correct.', tests/derive.rs:146:10
= note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
```

Validating your model at compile-time enables you to work on ASN.1 code without fear that you're unintentionally changing something in the background. I bet you're wondering now though, how we are supposed to have a struct with two strings for fields? The answer is thankfully pretty simple, you just add `#[rasn(tag)]` attribute to override the tags of one or more of the types. However we can actually go further, because in ASN.1 there's the concept of having `AUTOMATIC TAGS` which essentially tells your ASN.1 compiler to automatically generate distinct tags for your ASN.1 definition. Now with rasn you can do that in Rust! Applying `#[rasn(automatic_tags)]` to the container automatically generate tags will apply the same automatic tagging transformation you'd expect from an ASN.1 compiler.
Validating your model at compile-time enables you to work on ASN.1 code without fear that you're unintentionally changing something in the background. I bet you're wondering now though, how we are supposed to have a struct with two strings for fields? The answer is thankfully pretty simple, you just add `#[rasn(tag)]` attribute to override the tags of one or more of the types. However we can actually go further, because in ASN.1 there's the concept of having `AUTOMATIC TAGS` which essentially tells your ASN.1 compiler to automatically generate distinct tags for your ASN.1 definition. Now with rasn you can do that in Rust! Applying `#[rasn(automatic_tags)]` to the container will apply the same automatic tagging transformation you'd expect from an ASN.1 compiler.

```rust
use rasn::AsnType;
Expand Down

0 comments on commit ab3dc7a

Please sign in to comment.