Skip to content

Commit

Permalink
Nullables support (#7)
Browse files Browse the repository at this point in the history
* fix for nullables and added info about C# version support

* Update README.md
  • Loading branch information
ewerspej authored Oct 10, 2024
1 parent 03055f3 commit c0b6af8
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 36 deletions.
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,19 @@ If the `AudiPage` would ever get renamed to some other class name, you would ins

There may be situations where you need to be able to specify extra routes, e.g. when a route doesn't follow the specified naming convention using the suffix or when a routes is defined in a different way (in MAUI or Xamarin.Forms this could be using a `<ShellContent>` element in XAML).

For situations like these, the Route Generator exposes a second attribute called `[ExtraRoute]` and it takes a single argument representing the name of the route. You may not pass null, empty strings or whitespace as well as special characters. Duplicates will be ignored.
For situations like these, the Route Generator exposes a second attribute called `[ExtraRoute]` and it takes a single argument representing the name of the route. You should not pass `null`, empty strings, any whitespace or special characters. Duplicates will be ignored.

If an extra route is specified whose name doesn't match any existing class name, you will have to provide a type to the attribute in order to include it in the generated `Routes.RouteTypeMap` dictionary using `typeof(SomeClass)`.

```c#
namespace RouteGeneratorSample;

[AutoRoutes("Page")]
[ExtraRoute("SomeOtherRoute")] // valid
[ExtraRoute("SomeFaulty!Route")] // invalid
[ExtraRoute("YetAnotherRoute", typeof(MainPage))] // valid
[ExtraRoute("YetAnotherRoute")] // ignored, because it's a duplicate
[ExtraRoute("SomeFaulty!Route")] // invalid, will emit warning EXR001 and will be ignored
[ExtraRoute("YetAnotherRoute", typeof(MainPage))]
[ExtraRoute("YetAnotherRoute")] // duplicate, will emit warning EXR002 and will be ignored
[ExtraRoute("SomeOtherRoute")] // valid, but no corresponding type available, will emit warning EXR003
[ExtraRoute(null)] // will be ignored
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
Expand Down Expand Up @@ -154,7 +155,7 @@ namespace RouteGeneratorSample

## Route registration (e.g. in .NET MAUI)

Inspired by a comment by [Miguel Delgado](https://github.com/mdelgadov), version *1.0.1* introduces a new `Routes.RouteTypeMap` dictionary that maps route names to their respective Type. This can be used to register routes like this:
Inspired by a comment by [Miguel Delgado](https://github.com/mdelgadov), version *1.0.1* introduced a new `Routes.RouteTypeMap` dictionary that maps route names to their respective Type. This can be used to register routes like this:

```c#
foreach (var route in Routes.RouteTypeMap)
Expand Down Expand Up @@ -182,9 +183,17 @@ The Route Generator is featured in the following resources:
- Automatic registration of Pages and ViewModels as services
- Generation of route-specific extensions or methods (e.g. `Shell.Current.GoToMyAwesomePage(params)`)

# Remarks about Native AOT support
# Remarks

While Native AOT is still experimental in .NET 8.0 (e.g., it's not supported for Android yet and even iOS still is experiencing some hiccups), the latest version of Route Generator should technically be [AOT-compatible](https://learn.microsoft.com/dotnet/core/deploying/native-aot#limitations-of-native-aot-deployment). However, I cannot test this properly while there are still issues. Full Native AOT support will probably only be available with .NET 9.0 or higher: https://github.com/dotnet/maui/issues/18839#issuecomment-1828006233
## C# version compatibility

This source generator only works with **C# 10.0** or higher. If you are using **.NET 5.0 or below**, you will need to specify `<LangVersion>10.0</LangVersion>` in your project file.

The source generator is compatible with nullable reference types, the `[ExtraRoute]` attribute uses a `Type?` property. Please let me know if you run into problems with this.

## Native AOT support

While Native AOT is still experimental in .NET 8.0 (e.g., it's not supported for Android yet and even iOS still is experiencing some hiccups), the latest version of Route Generator should technically be [AOT-compatible](https://learn.microsoft.com/dotnet/core/deploying/native-aot#limitations-of-native-aot-deployment). However, I cannot test this properly while there are still issues. Full Native AOT support will probably only be available with .NET 9.0 or higher according to this [issue on GitHub](https://github.com/dotnet/maui/issues/18839#issuecomment-1828006233).

# Support

Expand Down
6 changes: 3 additions & 3 deletions RouteGeneratorSample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
namespace RouteGeneratorSample;

[AutoRoutes("Page")]
[ExtraRoute("SomeOtherRoute")]
[ExtraRoute("SomeFaulty!Route")]
[ExtraRoute("SomeFaulty!Route")] // invalid, will emit warning EXR001 and will be ignored
[ExtraRoute("YetAnotherRoute", typeof(MainPage))]
[ExtraRoute("YetAnotherRoute")]
[ExtraRoute("YetAnotherRoute")] // duplicate, will emit warning EXR002 and will be ignored
[ExtraRoute("SomeOtherRoute")] // valid, but no corresponding type available, will emit warning EXR003
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
Expand Down
11 changes: 7 additions & 4 deletions RouteGeneratorSampleConsole/Main.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using epj.RouteGenerator;
using System;
using epj.RouteGenerator;
using RouteGeneratorSampleConsole.Route;

namespace RouteGeneratorSampleConsole
{
[AutoRoutes("Route")]
[ExtraRoute(nameof(Fastest))]
[ExtraRoute("Inval!dRoute")]
[ExtraRoute("RouteWithoutAType")]
[ExtraRoute("Inval!dRoute")] // invalid, will emit warning EXR001 and will be ignored
[ExtraRoute("RouteWithAView", typeof(Fastest))]
[ExtraRoute("RouteWithAView")]
[ExtraRoute("RouteWithAView")] // duplicate, will emit warning EXR002 and will be ignored
[ExtraRoute("RouteWithoutAType")] // no type available, will emit warning EXR003
[ExtraRoute("RouteWithNull", null)] // no valid type available, will emit warning EXR003
[ExtraRoute(null)] // will be ignored
public static class Main
{
public static void PrintRoutes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<!--<PropertyGroup>
Expand Down
43 changes: 25 additions & 18 deletions epj.RouteGenerator/RouteGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,32 +204,33 @@ private void AddExtraRoutes(SourceProductionContext context, Compilation compila
private static string BuildSource(IReadOnlyCollection<string> routeNameList, Dictionary<string, string> routesAndNamespacesDictionary, string namespaceName)
{
var routeClassDeclarationStrings = routeNameList.Select(page => $"public const string {page} = \"{page}\";");
var routeClassDeclarationsString = string.Join("\n ", routeClassDeclarationStrings);
var routeClassDeclarationsString = string.Join("\n ", routeClassDeclarationStrings);

var source = $$"""
// <auto-generated/>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace {{namespaceName}}
namespace {{namespaceName}};
public static class Routes
{
public static class Routes
{{routeClassDeclarationsString}}
private static List<string> allRoutes = new()
{
{{routeClassDeclarationsString}}
{{string.Join(",\n ", routeNameList)}}
};
private static List<string> allRoutes = new()
{
{{string.Join(",\n ", routeNameList)}}
};
public static ReadOnlyCollection<string> AllRoutes => allRoutes.AsReadOnly();
private static Dictionary<string, Type> routeTypeMap = new()
{
{{string.Join(",\n ", routesAndNamespacesDictionary.Select(x => $"{{{x.Key},typeof({x.Value})}}"))}}
};
public static ReadOnlyDictionary<string, Type> RouteTypeMap => routeTypeMap.AsReadOnly();
}
public static ReadOnlyCollection<string> AllRoutes => allRoutes.AsReadOnly();
private static Dictionary<string, Type> routeTypeMap = new()
{
{{string.Join(",\n ", routesAndNamespacesDictionary.Select(x => $"{{ {x.Key}, typeof({x.Value}) }}"))}}
};
public static ReadOnlyDictionary<string, Type> RouteTypeMap => routeTypeMap.AsReadOnly();
}
""";
return source;
Expand Down Expand Up @@ -257,6 +258,7 @@ private static IEnumerable<INamedTypeSymbol> GetAllClasses(INamespaceSymbol name
private static string BuildAutoRoutesAttribute()
{
return """
// <auto-generated/>
using System;
namespace epj.RouteGenerator;
Expand All @@ -277,6 +279,9 @@ public AutoRoutesAttribute(string suffix)
private static string BuildExtraRouteAttribute()
{
return """
// <auto-generated/>
#nullable enable
using System;
namespace epj.RouteGenerator;
Expand All @@ -298,6 +303,8 @@ public ExtraRouteAttribute(string route, Type typename)
Typename = typename;
}
}
#nullable disable
""";
}
}
2 changes: 1 addition & 1 deletion epj.RouteGenerator/epj.RouteGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>epj.RouteGenerator</PackageId>
<Title>Route Generator for .NET apps</Title>
<Version>1.0.1</Version>
<Version>1.0.2-preview1</Version>
<Authors>ewerspej</Authors>
<Description>Automatically generates route names for navigation based on a class name convention for .NET applications</Description>
<PackageProjectUrl>https://github.com/ewerspej/epj.RouteGenerator</PackageProjectUrl>
Expand Down

0 comments on commit c0b6af8

Please sign in to comment.