Libraries and CLIs for my personal all-in-one productivity system including components like bookmarks, notes, todos, projects, etc.

img Conventional Commits pre-commit

bntp.go

Libraries and CLIs for my personal all-in-one productivity system including components like bookmarks, notes, todos, projects, etc.

Neovim integration available at https://github.com/JonasMuehlmann/bntp.nvim

Why?

Being a huge productivity nerd and life long learner, I value having efficient tools and workflows but have not found any one tool, which satisfied my needs. Combining multiple tools is suboptimal too, even though using Notion and Todoist has serverd me quite alright so far. Being a software developer, I thought, I would use my skills to my advantage and build the very tool I am trying to find, implementing just what I need, exactly how I want it and combining the best of all the other tools out there while having complete control and not depending on others.

Tools and systems, which have or, after research and considerations, might influence this project:

  • Notion
  • Roam Research
  • Obsidian
  • Todoist
  • The Zettelkasten Method
  • David Allens Getting Things Done (GTD)

Goals

  • Synergy by using a single tool serving as a complete productivity system
  • No reliance on external services (local data synced through my own means)
  • Integrate with and built upon my neovim/terminal based workflows and tools (no bloated and unergonomic graphical or web based interfaces)
  • Scriptable (e.g. periodic bookmark imports)
  • Modular (Interfaces built on top of CLIs built on top of different libraries)
  • Portable through import/export to/from and use of common file formats (YML,CSV,Markdown) using a local SQLite database only where it makes sense
  • Discoverable through context given by a hierarchical tag structure fuzzy searching and using filters
  • Extensive association through links/backlinks between documents and declaration of sources and related pages
  • High performance by working completely offline, having no bloat and using a high performance backend written in go
  • Quick and smooth usage because of all of the above(especially the neovim frontend)

Features

  • Bidirectional linking between documents (creating a link to B inside A creates a backlink to A in B)
  • Hierarchical tag structure with YML import/export (tags can have sub tags: software_development has sub tags software_engineering and computer_science, searching for software_developmebt also shows documents with the tags software_engineering and computer_science)

Content types

  • Bookmarks with CSV import/export
  • Todos with CSV import/export
  • Documents (Markdown based files categorized for different purposes)
    • Notes
    • Projects (notes with associated todos)
    • Roadmaps (collection of projects, notes, todos and bookmarks)
Owner
Jonas Mühlmann
I am a 20 years old student attending the Bib International College in Paderborn, Germany.
Jonas Mühlmann
Comments
  • make sure to insert all tags in ParentPath and Subtags into db

    make sure to insert all tags in ParentPath and Subtags into db

    make sure to insert all tags in ParentPath and Subtags into db

    Filter Converter //

    ******************************************************************//

    Updater Converter //

    ******************************************************************//

    https://github.com/JonasMuehlmann/bntp.go/blob/a11b2499090d4ad434bbafe62f0312f488491758/model/repository/sqlite3/tag_repository.go#L765

    
            return []*domain.Tag{}, err
        }
    
        domainModels := make([]*domain.Tag, 0, len(repositoryModels))
    
        for _, repoModel := range repositoryModels {
            domainModel, err := repo.TagRepositoryToDomainModel(ctx, repoModel)
            if err != nil {
                return domainModels, err
            }
    
            domainModels = append(domainModels, domainModel)
        }
    
        return domainModels, err
    }
    
    
    
    
    //******************************************************************//
    //                            Converters                            //
    //******************************************************************//
    func (repo *Sqlite3TagRepository) GetTagDomainToRepositoryModel(ctx context.Context) func(domainModel *domain.Tag) (repositoryModel any, err error) {
        return func(domainModel *domain.Tag) (repositoryModel any, err error) {
            return repo.TagDomainToRepositoryModel(ctx, domainModel)
        }
    }
    
    func (repo *Sqlite3TagRepository) GetTagRepositoryToDomainModel(ctx context.Context) func(repositoryModel any) (domainModel *domain.Tag, err error) {
        return func(repositoryModel any) (domainModel *domain.Tag, err error) {
    
            return repo.TagRepositoryToDomainModel(ctx,repositoryModel)
        }
    }
    
    
    //******************************************************************//
    //                          Model Converter                         //
    //******************************************************************//
    
    
    
    func (repo *Sqlite3TagRepository) TagDomainToRepositoryModel(ctx context.Context, domainModel *domain.Tag) (repositoryModel any, err error)  {
    
    // TODO: make sure to insert all tags in ParentPath and Subtags into db
        repositoryModelConcrete := new(Tag)
        repositoryModelConcrete.R = repositoryModelConcrete.R.NewStruct()
    
        repositoryModelConcrete.ID = domainModel.ID
        repositoryModelConcrete.Tag = domainModel.Tag
    
    
    //***********************    Set ParentTag    **********************//
        if len(domainModel.ParentPath) > 0 {
            repositoryModelConcrete.ParentTag = null.NewInt64(domainModel.ParentPath[len(domainModel.ParentPath) - 1].ID, true)
        }
    
    //*************************    Set Path    *************************//
    for _, tag := range domainModel.ParentPath {
        repositoryModelConcrete.Path += strconv.FormatInt(tag.ID, 10) + ";"
    }
    
    repositoryModelConcrete.Path += strconv.FormatInt(domainModel.ID, 10)
    
    //************************    Set Children  ************************//
    for _, tag := range domainModel.Subtags {
        repositoryModelConcrete.Children += strconv.FormatInt(tag.ID, 10) + ";"
    }
    
        repositoryModel = repositoryModelConcrete
    
        return
    }
    
    // TODO: These functions should be context aware
    func (repo *Sqlite3TagRepository) TagRepositoryToDomainModel(ctx context.Context, repositoryModel any) (domainModel *domain.Tag, err error) {
    // TODO: make sure to insert all tags in ParentPath and Subtags into db
        domainModel = new(domain.Tag)
    
        repositoryModelConcrete := repositoryModel.(Tag)
    
        domainModel.ID = repositoryModelConcrete.ID
        domainModel.Tag = repositoryModelConcrete.Tag
    
    //***********************    Set ParentPath    **********************//
    var parentTagID int64
    var parentTag *Tag
    var domainParentTag *domain.Tag
    
    for _, parentTagIDRaw := range strings.Split(repositoryModelConcrete.Path, ";")[:len(repositoryModelConcrete.Path)-2]{
        parentTagID, err = strconv.ParseInt(parentTagIDRaw, 10, 64)
        if err != nil {
            return
        }
    
        parentTag, err = Tags(TagWhere.ID.EQ(parentTagID)).One(ctx, repo.db)
        if err != nil {
            return
        }
    
        domainParentTag, err = repo.TagRepositoryToDomainModel(ctx, parentTag)
        if err != nil {
            return
        }
    
        domainModel.ParentPath = append(domainModel.ParentPath, domainParentTag)
    }
    
    //************************    Set Subtags ************************//
    var childTagID int64
    var childTag *Tag
    var domainChildTag *domain.Tag
    
    for _, childTagIDRaw := range strings.Split(repositoryModelConcrete.Children, ";")[:len(repositoryModelConcrete.Children)-2]{
        childTagID, err = strconv.ParseInt(childTagIDRaw, 10, 64)
        if err != nil {
            return
        }
    
        childTag, err = Tags(TagWhere.ID.EQ(childTagID)).One(ctx, repo.db)
        if err != nil {
            return
        }
    
        domainChildTag, err = repo.TagRepositoryToDomainModel(ctx, childTag)
        if err != nil {
            return
        }
    
        domainModel.Subtags = append(domainModel.Subtags, domainChildTag)
    }
    
        repositoryModel = repositoryModelConcrete
    
        return
    }
    
    //******************************************************************//
    //                         Filter Converter                         //
    //******************************************************************//
    
    
    
    func (repo *Sqlite3TagRepository) TagDomainToRepositoryFilter(ctx context.Context, domainFilter *domain.TagFilter) (repositoryFilter any, err error)  {
        repositoryFilterConcrete := new(TagFilter)
    
    	repositoryFilterConcrete.ID = domainFilter.ID
    	repositoryFilterConcrete.Tag = domainFilter.Tag
    
    	if domainFilter.ParentPath.HasValue {
    
    		//*********************    Set ParentPath    *********************//
    		var convertedParentTagTagFilter model.FilterOperation[*Tag]
    
    		convertedParentTagTagFilter, err = model.ConvertFilter[*Tag, *domain.Tag](domainFilter.ParentPath.Wrappee, repoCommon.MakeDomainToRepositoryEntityConverterGeneric[domain.Tag, Tag](ctx, repo.TagDomainToRepositoryModel))
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.ParentTagTag.Push(convertedParentTagTagFilter)
    		//*************************    Set Path    *************************//
    		var convertedPathFilter model.FilterOperation[string]
    
    		convertedPathFilter, err = model.ConvertFilter[string, *domain.Tag](domainFilter.ParentPath.Wrappee, func(tag *domain.Tag) (string, error) { return strconv.FormatInt(tag.ID, 10), nil })
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.Path.Push(convertedPathFilter)
    		//**********************    Set ParentTag    ***********************//
    		var convertedParentTagFilter model.FilterOperation[null.Int64]
    
    		convertedParentTagFilter, err = model.ConvertFilter[null.Int64, *domain.Tag](domainFilter.ParentPath.Wrappee, func(tag *domain.Tag) (null.Int64, error) { return null.NewInt64(tag.ID, true), nil })
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.ParentTag.Push(convertedParentTagFilter)
    	}
    
    	//**********************    Set child tags *********************//
    	if domainFilter.Subtags.HasValue {
    		var convertedFilter model.FilterOperation[string]
    
    		convertedFilter, err = model.ConvertFilter[string, *domain.Tag](domainFilter.Subtags.Wrappee, func(tag *domain.Tag) (string, error) { return strconv.FormatInt(tag.ID, 10), nil })
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.Children.Push(convertedFilter)
    	}
    
        repositoryFilter = repositoryFilterConcrete
    
    	return
    }
    
    //******************************************************************//
    //                         Updater Converter                        //
    //******************************************************************//
    
    
    
    func (repo *Sqlite3TagRepository) TagDomainToRepositoryUpdater(ctx context.Context, domainUpdater *domain.TagUpdater) (repositoryUpdater any, err error)  {
        repositoryUpdaterConcrete := new(TagUpdater)
    
    	//**************************    Set tag    *************************//
    	if domainUpdater.Tag.HasValue {
    		repositoryUpdaterConcrete.Tag.Push(model.UpdateOperation[string]{Operator: domainUpdater.Tag.Wrappee.Operator, Operand: repositoryUpdaterConcrete.Tag.Wrappee.Operand})
    	}
    
    	//***********    Set ParentPath    ***********//
    	if domainUpdater.ParentPath.HasValue {
    		var convertedTagRaw any
    		tag := domainUpdater.ParentPath.Wrappee.Operand[len(domainUpdater.ParentPath.Wrappee.Operand)-1]
    		convertedTagRaw, err =  repo.TagDomainToRepositoryModel(ctx, tag)
    		if err != nil {
    			return
    		}
    
    		repositoryUpdaterConcrete.ParentTagTag.Push(model.UpdateOperation[*Tag]{Operator: domainUpdater.ParentPath.Wrappee.Operator, Operand: convertedTagRaw.(*Tag)})
    		repositoryUpdaterConcrete.ParentTag.Push(model.UpdateOperation[null.Int64]{Operator: domainUpdater.ParentPath.Wrappee.Operator, Operand: null.NewInt64(convertedTagRaw.(*Tag).ID, true)})
    
    		pathIDs := make([]string, 0, len(domainUpdater.ParentPath.Wrappee.Operand)+1)
    		for _, tag := range domainUpdater.ParentPath.Wrappee.Operand {
    			pathIDs = append(pathIDs, strconv.FormatInt(tag.ID, 10))
    		}
    
    		pathIDs = append(pathIDs, strconv.FormatInt(tag.ID, 10))
    
    		repositoryUpdaterConcrete.Path.Push(model.UpdateOperation[string]{Operator: domainUpdater.ParentPath.Wrappee.Operator, Operand: strings.Join(pathIDs, ";")})
    	}
    
    	//***********************    Set Children    ***********************//
    	if domainUpdater.Subtags.HasValue {
    		pathIDs := make([]string, 0, len(domainUpdater.Subtags.Wrappee.Operand)+1)
    		for _, tag := range domainUpdater.Subtags.Wrappee.Operand {
    			pathIDs = append(pathIDs, strconv.FormatInt(tag.ID, 10))
    		}
    
    		repositoryUpdaterConcrete.Children.Push(model.UpdateOperation[string]{Operator: domainUpdater.Subtags.Wrappee.Operator, Operand: strings.Join(pathIDs, ";")})
    	}
    
    	//**************************    Set ID    **************************//
    	if domainUpdater.ID.HasValue {
    		repositoryUpdaterConcrete.ID.Push(model.UpdateOperation[int64]{Operator: domainUpdater.ID.Wrappee.Operator, Operand: repositoryUpdaterConcrete.ID.Wrappee.Operand})
    	}
    
        repositoryUpdater = repositoryUpdaterConcrete
    
    	return
    }
    
    
    

    df60b12c26a8b5b739594b922035da37f4608da0

  • These functions should be context aware

    These functions should be context aware

    These functions should be context aware

    https://github.com/JonasMuehlmann/bntp.go/blob/a11b2499090d4ad434bbafe62f0312f488491758/model/repository/sqlite3/tag_repository.go#L795

    
            return []*domain.Tag{}, err
        }
    
        domainModels := make([]*domain.Tag, 0, len(repositoryModels))
    
        for _, repoModel := range repositoryModels {
            domainModel, err := repo.TagRepositoryToDomainModel(ctx, repoModel)
            if err != nil {
                return domainModels, err
            }
    
            domainModels = append(domainModels, domainModel)
        }
    
        return domainModels, err
    }
    
    
    
    
    //******************************************************************//
    //                            Converters                            //
    //******************************************************************//
    func (repo *Sqlite3TagRepository) GetTagDomainToRepositoryModel(ctx context.Context) func(domainModel *domain.Tag) (repositoryModel any, err error) {
        return func(domainModel *domain.Tag) (repositoryModel any, err error) {
            return repo.TagDomainToRepositoryModel(ctx, domainModel)
        }
    }
    
    func (repo *Sqlite3TagRepository) GetTagRepositoryToDomainModel(ctx context.Context) func(repositoryModel any) (domainModel *domain.Tag, err error) {
        return func(repositoryModel any) (domainModel *domain.Tag, err error) {
    
            return repo.TagRepositoryToDomainModel(ctx,repositoryModel)
        }
    }
    
    
    //******************************************************************//
    //                          Model Converter                         //
    //******************************************************************//
    
    
    
    func (repo *Sqlite3TagRepository) TagDomainToRepositoryModel(ctx context.Context, domainModel *domain.Tag) (repositoryModel any, err error)  {
    
    // TODO: make sure to insert all tags in ParentPath and Subtags into db
        repositoryModelConcrete := new(Tag)
        repositoryModelConcrete.R = repositoryModelConcrete.R.NewStruct()
    
        repositoryModelConcrete.ID = domainModel.ID
        repositoryModelConcrete.Tag = domainModel.Tag
    
    
    //***********************    Set ParentTag    **********************//
        if len(domainModel.ParentPath) > 0 {
            repositoryModelConcrete.ParentTag = null.NewInt64(domainModel.ParentPath[len(domainModel.ParentPath) - 1].ID, true)
        }
    
    //*************************    Set Path    *************************//
    for _, tag := range domainModel.ParentPath {
        repositoryModelConcrete.Path += strconv.FormatInt(tag.ID, 10) + ";"
    }
    
    repositoryModelConcrete.Path += strconv.FormatInt(domainModel.ID, 10)
    
    //************************    Set Children  ************************//
    for _, tag := range domainModel.Subtags {
        repositoryModelConcrete.Children += strconv.FormatInt(tag.ID, 10) + ";"
    }
    
        repositoryModel = repositoryModelConcrete
    
        return
    }
    
    // TODO: These functions should be context aware
    func (repo *Sqlite3TagRepository) TagRepositoryToDomainModel(ctx context.Context, repositoryModel any) (domainModel *domain.Tag, err error) {
    // TODO: make sure to insert all tags in ParentPath and Subtags into db
        domainModel = new(domain.Tag)
    
        repositoryModelConcrete := repositoryModel.(Tag)
    
        domainModel.ID = repositoryModelConcrete.ID
        domainModel.Tag = repositoryModelConcrete.Tag
    
    //***********************    Set ParentPath    **********************//
    var parentTagID int64
    var parentTag *Tag
    var domainParentTag *domain.Tag
    
    for _, parentTagIDRaw := range strings.Split(repositoryModelConcrete.Path, ";")[:len(repositoryModelConcrete.Path)-2]{
        parentTagID, err = strconv.ParseInt(parentTagIDRaw, 10, 64)
        if err != nil {
            return
        }
    
        parentTag, err = Tags(TagWhere.ID.EQ(parentTagID)).One(ctx, repo.db)
        if err != nil {
            return
        }
    
        domainParentTag, err = repo.TagRepositoryToDomainModel(ctx, parentTag)
        if err != nil {
            return
        }
    
        domainModel.ParentPath = append(domainModel.ParentPath, domainParentTag)
    }
    
    //************************    Set Subtags ************************//
    var childTagID int64
    var childTag *Tag
    var domainChildTag *domain.Tag
    
    for _, childTagIDRaw := range strings.Split(repositoryModelConcrete.Children, ";")[:len(repositoryModelConcrete.Children)-2]{
        childTagID, err = strconv.ParseInt(childTagIDRaw, 10, 64)
        if err != nil {
            return
        }
    
        childTag, err = Tags(TagWhere.ID.EQ(childTagID)).One(ctx, repo.db)
        if err != nil {
            return
        }
    
        domainChildTag, err = repo.TagRepositoryToDomainModel(ctx, childTag)
        if err != nil {
            return
        }
    
        domainModel.Subtags = append(domainModel.Subtags, domainChildTag)
    }
    
        repositoryModel = repositoryModelConcrete
    
        return
    }
    
    //******************************************************************//
    //                         Filter Converter                         //
    //******************************************************************//
    
    
    
    func (repo *Sqlite3TagRepository) TagDomainToRepositoryFilter(ctx context.Context, domainFilter *domain.TagFilter) (repositoryFilter any, err error)  {
        repositoryFilterConcrete := new(TagFilter)
    
    	repositoryFilterConcrete.ID = domainFilter.ID
    	repositoryFilterConcrete.Tag = domainFilter.Tag
    
    	if domainFilter.ParentPath.HasValue {
    
    		//*********************    Set ParentPath    *********************//
    		var convertedParentTagTagFilter model.FilterOperation[*Tag]
    
    		convertedParentTagTagFilter, err = model.ConvertFilter[*Tag, *domain.Tag](domainFilter.ParentPath.Wrappee, repoCommon.MakeDomainToRepositoryEntityConverterGeneric[domain.Tag, Tag](ctx, repo.TagDomainToRepositoryModel))
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.ParentTagTag.Push(convertedParentTagTagFilter)
    		//*************************    Set Path    *************************//
    		var convertedPathFilter model.FilterOperation[string]
    
    		convertedPathFilter, err = model.ConvertFilter[string, *domain.Tag](domainFilter.ParentPath.Wrappee, func(tag *domain.Tag) (string, error) { return strconv.FormatInt(tag.ID, 10), nil })
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.Path.Push(convertedPathFilter)
    		//**********************    Set ParentTag    ***********************//
    		var convertedParentTagFilter model.FilterOperation[null.Int64]
    
    		convertedParentTagFilter, err = model.ConvertFilter[null.Int64, *domain.Tag](domainFilter.ParentPath.Wrappee, func(tag *domain.Tag) (null.Int64, error) { return null.NewInt64(tag.ID, true), nil })
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.ParentTag.Push(convertedParentTagFilter)
    	}
    
    	//**********************    Set child tags *********************//
    	if domainFilter.Subtags.HasValue {
    		var convertedFilter model.FilterOperation[string]
    
    		convertedFilter, err = model.ConvertFilter[string, *domain.Tag](domainFilter.Subtags.Wrappee, func(tag *domain.Tag) (string, error) { return strconv.FormatInt(tag.ID, 10), nil })
    		if err != nil {
    			return
    		}
    
    		repositoryFilterConcrete.Children.Push(convertedFilter)
    	}
    
        repositoryFilter = repositoryFilterConcrete
    
    	return
    }
    
    //******************************************************************//
    //                         Updater Converter                        //
    //******************************************************************//
    
    
    
    func (repo *Sqlite3TagRepository) TagDomainToRepositoryUpdater(ctx context.Context, domainUpdater *domain.TagUpdater) (repositoryUpdater any, err error)  {
        repositoryUpdaterConcrete := new(TagUpdater)
    
    	//**************************    Set tag    *************************//
    	if domainUpdater.Tag.HasValue {
    		repositoryUpdaterConcrete.Tag.Push(model.UpdateOperation[string]{Operator: domainUpdater.Tag.Wrappee.Operator, Operand: repositoryUpdaterConcrete.Tag.Wrappee.Operand})
    	}
    
    	//***********    Set ParentPath    ***********//
    	if domainUpdater.ParentPath.HasValue {
    		var convertedTagRaw any
    		tag := domainUpdater.ParentPath.Wrappee.Operand[len(domainUpdater.ParentPath.Wrappee.Operand)-1]
    		convertedTagRaw, err =  repo.TagDomainToRepositoryModel(ctx, tag)
    		if err != nil {
    			return
    		}
    
    		repositoryUpdaterConcrete.ParentTagTag.Push(model.UpdateOperation[*Tag]{Operator: domainUpdater.ParentPath.Wrappee.Operator, Operand: convertedTagRaw.(*Tag)})
    		repositoryUpdaterConcrete.ParentTag.Push(model.UpdateOperation[null.Int64]{Operator: domainUpdater.ParentPath.Wrappee.Operator, Operand: null.NewInt64(convertedTagRaw.(*Tag).ID, true)})
    
    		pathIDs := make([]string, 0, len(domainUpdater.ParentPath.Wrappee.Operand)+1)
    		for _, tag := range domainUpdater.ParentPath.Wrappee.Operand {
    			pathIDs = append(pathIDs, strconv.FormatInt(tag.ID, 10))
    		}
    
    		pathIDs = append(pathIDs, strconv.FormatInt(tag.ID, 10))
    
    		repositoryUpdaterConcrete.Path.Push(model.UpdateOperation[string]{Operator: domainUpdater.ParentPath.Wrappee.Operator, Operand: strings.Join(pathIDs, ";")})
    	}
    
    	//***********************    Set Children    ***********************//
    	if domainUpdater.Subtags.HasValue {
    		pathIDs := make([]string, 0, len(domainUpdater.Subtags.Wrappee.Operand)+1)
    		for _, tag := range domainUpdater.Subtags.Wrappee.Operand {
    			pathIDs = append(pathIDs, strconv.FormatInt(tag.ID, 10))
    		}
    
    		repositoryUpdaterConcrete.Children.Push(model.UpdateOperation[string]{Operator: domainUpdater.Subtags.Wrappee.Operator, Operand: strings.Join(pathIDs, ";")})
    	}
    
    	//**************************    Set ID    **************************//
    	if domainUpdater.ID.HasValue {
    		repositoryUpdaterConcrete.ID.Push(model.UpdateOperation[int64]{Operator: domainUpdater.ID.Wrappee.Operator, Operand: repositoryUpdaterConcrete.ID.Wrappee.Operand})
    	}
    
        repositoryUpdater = repositoryUpdaterConcrete
    
    	return
    }
    
    
    

    f39223360281ce40e2d4f92e12388cf6eeb26ca0

  • Ignore EmptyIterable errors in all goaoi calls

    Ignore EmptyIterable errors in all goaoi calls

    Ignore EmptyIterable errors in all goaoi calls

    https://github.com/JonasMuehlmann/bntp.go/blob/5d269e9350776b5904cc1eb6412cf082501b8378/model/repository/sqlite3/bookmark_repository.go#L391

    
    			panic(fmt.Sprintf("Expected type %T but got %T", BookmarkFilterMapping[any]{}, filter))
    		}
    
    		newQueryMod := buildQueryModFilterBookmark(filterMapping.Field, filterMapping.FilterOperation)
    
    		queryModList = append(queryModList, newQueryMod...)
    	}
    
    	return queryModList
    }
    
    func GetBookmarkDomainToSqlRepositoryModel(ctx context.Context, db *sql.DB) func(domainModel *domain.Bookmark) (sqlRepositoryModel *Bookmark, err error) {
    	return func(domainModel *domain.Bookmark) (sqlRepositoryModel *Bookmark, err error) {
    		return BookmarkDomainToSqlRepositoryModel(ctx, db, domainModel)
    	}
    }
    
    func GetBookmarkSqlRepositoryToDomainModel(ctx context.Context, db *sql.DB) func(repositoryModel *Bookmark) (domainModel *domain.Bookmark, err error) {
    	return func(sqlRepositoryModel *Bookmark) (domainModel *domain.Bookmark, err error) {
    		return BookmarkSqlRepositoryToDomainModel(ctx, db, sqlRepositoryModel)
    	}
    }
    
    type Sqlite3BookmarkRepositoryConstructorArgs struct {
    	DB *sql.DB
    }
    
    func (repo *Sqlite3BookmarkRepository) New(args any) (repositoryCommon.BookmarkRepository, error) {
    	constructorArgs, ok := args.(Sqlite3BookmarkRepositoryConstructorArgs)
    	if !ok {
    		return repo, fmt.Errorf("expected type %T but got %T", Sqlite3BookmarkRepositoryConstructorArgs{}, args)
    	}
    
    	repo.db = constructorArgs.DB
    
    	return repo, nil
    }
    
    func (repo *Sqlite3BookmarkRepository) Add(ctx context.Context, domainModels []*domain.Bookmark) error {
    	repositoryModels, err := goaoi.TransformCopySlice(domainModels, GetBookmarkDomainToSqlRepositoryModel(ctx, repo.db))
    	// TODO: Ignore EmptyIterable errors in all goaoi calls
    	if err != nil {
    		return err
    	}
    
    

    6c1e070e289301af0b7843f213c011c5b7cb8415

  • Implement output filters

    Implement output filters

    Implement output filters

    https://github.com/JonasMuehlmann/bntp.go/blob/6d052c29f92befeecb1e86509888ed6acde826c2/cmd/bookmark.go#L181

    
    }
    
    var bookmarkAddCmd = &cobra.Command{
    	Use:   "add MODEL...",
    	Short: "Add a bntp bookmark",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err := BNTPBackend.BookmarkManager.Add(context.Background(), bookmarks)
    		if err != nil {
    			panic(err)
    		}
    	},
    }
    
    var bookmarkReplaceCmd = &cobra.Command{
    	Use:   "replace MODEL...",
    	Short: "Replace a bntp bookmark with an updated version",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err := BNTPBackend.BookmarkManager.Replace(context.Background(), bookmarks)
    		if err != nil {
    			panic(err)
    		}
    	},
    }
    
    var bookmarkUpsertCmd = &cobra.Command{
    	Use:   "upsert MODEL...",
    	Short: "Add or replace a bntp bookmark",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err := BNTPBackend.BookmarkManager.Upsert(context.Background(), bookmarks)
    		if err != nil {
    			panic(err)
    		}
    	},
    }
    
    var bookmarkEditCmd = &cobra.Command{
    	Use:   "edit MODEL...",
    	Short: "Edit a bntp bookmark",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		var err error
    		var filter *domain.BookmarkFilter
    		var updater *domain.BookmarkUpdater
    		var numAffectedRecords int64
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err = BNTPBackend.Unmarshallers[Format].Unmarshall(updater, UpdaterRaw)
    		if err != nil {
    			panic(err)
    		}
    		// TODO: We should also have Update and Upsert methods in the managers and repositories
    		if FilterRaw == "" {
    			err := BNTPBackend.BookmarkManager.Update(context.Background(), bookmarks, updater)
    			if err != nil {
    				panic(err)
    			}
    
    			numAffectedRecords = int64(len(args))
    		} else {
    			err = BNTPBackend.Unmarshallers[Format].Unmarshall(filter, FilterRaw)
    			if err != nil {
    				panic(err)
    			}
    
    			numAffectedRecords, err = BNTPBackend.BookmarkManager.UpdateWhere(context.Background(), filter, updater)
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		fmt.Println(numAffectedRecords)
    	},
    }
    
    // TODO: Implement output filters
    var bookmarkListCmd = &cobra.Command{
    	Use:   "list",
    	Short: "List bntp bookmarks",
    
    

    ebd67247bd01a3833b03eaa3b07c2c3e6ce4e4c9

  • We should also have Update and Upsert methods in the managers and repositories

    We should also have Update and Upsert methods in the managers and repositories

    We should also have Update and Upsert methods in the managers and repositories

    https://github.com/JonasMuehlmann/bntp.go/blob/6d052c29f92befeecb1e86509888ed6acde826c2/cmd/bookmark.go#L157

    
    }
    
    var bookmarkAddCmd = &cobra.Command{
    	Use:   "add MODEL...",
    	Short: "Add a bntp bookmark",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err := BNTPBackend.BookmarkManager.Add(context.Background(), bookmarks)
    		if err != nil {
    			panic(err)
    		}
    	},
    }
    
    var bookmarkReplaceCmd = &cobra.Command{
    	Use:   "replace MODEL...",
    	Short: "Replace a bntp bookmark with an updated version",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err := BNTPBackend.BookmarkManager.Replace(context.Background(), bookmarks)
    		if err != nil {
    			panic(err)
    		}
    	},
    }
    
    var bookmarkUpsertCmd = &cobra.Command{
    	Use:   "upsert MODEL...",
    	Short: "Add or replace a bntp bookmark",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err := BNTPBackend.BookmarkManager.Upsert(context.Background(), bookmarks)
    		if err != nil {
    			panic(err)
    		}
    	},
    }
    
    var bookmarkEditCmd = &cobra.Command{
    	Use:   "edit MODEL...",
    	Short: "Edit a bntp bookmark",
    	Long:  `A longer description`,
    	Args:  cobra.ArbitraryArgs,
    	Run: func(cmd *cobra.Command, args []string) {
    		if len(args) == 0 {
    			cmd.Help()
    			os.Exit(0)
    		}
    
    		var err error
    		var filter *domain.BookmarkFilter
    		var updater *domain.BookmarkUpdater
    		var numAffectedRecords int64
    
    		bookmarks := make([]*domain.Bookmark, 0, len(args))
    
    		for i, bookmarkOut := range bookmarks {
    			err := BNTPBackend.Unmarshallers[Format].Unmarshall(bookmarkOut, args[i])
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		err = BNTPBackend.Unmarshallers[Format].Unmarshall(updater, UpdaterRaw)
    		if err != nil {
    			panic(err)
    		}
    		// TODO: We should also have Update and Upsert methods in the managers and repositories
    		if FilterRaw == "" {
    			err := BNTPBackend.BookmarkManager.Update(context.Background(), bookmarks, updater)
    			if err != nil {
    				panic(err)
    			}
    
    			numAffectedRecords = int64(len(args))
    		} else {
    			err = BNTPBackend.Unmarshallers[Format].Unmarshall(filter, FilterRaw)
    			if err != nil {
    				panic(err)
    			}
    
    			numAffectedRecords, err = BNTPBackend.BookmarkManager.UpdateWhere(context.Background(), filter, updater)
    			if err != nil {
    				panic(err)
    			}
    		}
    
    		fmt.Println(numAffectedRecords)
    	},
    }
    
    // TODO: Implement output filters
    var bookmarkListCmd = &cobra.Command{
    	Use:   "list",
    	Short: "List bntp bookmarks",
    
    

    aace8e0f2351b8b136d4dcef0e909f0f5e921f5d

  • Reimplement libdocuments (file operations) here

    Reimplement libdocuments (file operations) here

    Reimplement libdocuments (file operations) here

    https://github.com/JonasMuehlmann/bntp.go/blob/b71f4520e4ae522ad48de0e8bfcdeb3b353e8a00/bntp/libdocuments/documentmanager.go#L36

    
    
    type DocumentManager struct {
    	Repository repository.DocumentRepository
    	// TODO: Reimplement libdocuments (file operations) here
    	ContentRepository repository.ContentFileRepository
    	Hooks             *bntp.Hooks[domain.Document]
    }
    
    func New(hooks *bntp.Hooks[domain.Document], repository repository.DocumentRepository) (DocumentManager, error) {
    
    

    6156cfc49d39b9e03f49db6541086c681d671039

  • Allow skipping certain hooks.

    Allow skipping certain hooks.

    Allow skipping certain hooks.

    https://github.com/JonasMuehlmann/bntp.go/blob/66b9bad1c6d2af149f08cdccd0ec49f607085f13/pkg/libbookmarks/bookmarkmanager.go#L32

    
    	bntp "github.com/JonasMuehlmann/bntp.go/pkg"
    )
    
    // TODO: Allow skipping certain hooks.
    type BookmarkManager struct {
    	hooks      bntp.Hooks[domain.Bookmark]
    	repository repository.BookmarkRepository
    }
    
    func (m *BookmarkManager) New(...any) (BookmarkManager, error) {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) Add(context.Context, []domain.Bookmark) (numAffectedRecords int, newID int, err error) {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) Replace(context.Context, []domain.Bookmark) error {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) UpdateWhere(context.Context, domain.BookmarkFilter, map[domain.BookmarkField]domain.BookmarkUpdater) (numAffectedRecords int, err error) {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) Delete(context.Context, []domain.Bookmark) error {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) DeleteWhere(context.Context, domain.BookmarkFilter) (numAffectedRecords int, err error) {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) CountWhere(context.Context, domain.BookmarkFilter) int {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) CountAll(context.Context) int {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) DoesExist(context.Context, domain.Bookmark) bool {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) DoesExistWhere(context.Context, domain.BookmarkFilter) bool {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) GetWhere(context.Context, domain.BookmarkFilter) []domain.Bookmark {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) GetFirstWhere(context.Context, domain.BookmarkFilter) domain.Bookmark {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) GetAll(context.Context) []domain.Bookmark {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) AddType(context.Context, string) error {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) DeleteType(context.Context, string) error {
    	panic("Not implemented")
    }
    
    func (m *BookmarkManager) UpdateType(context.Context, string, string) error {
    	panic("Not implemented")
    }
    
    

    d0664f443967ff52b3be8c23b6469bb069283d0e

  • Add template for type safe config struct per provider for New() methods

    Add template for type safe config struct per provider for New() methods

    Add template for type safe config struct per provider for New() methods

    It could embed a generic RepositoryConfig into repository specific configurations e.g. Sqlite3RepositoryConfig

    https://github.com/JonasMuehlmann/bntp.go/blob/fed1c8852ea770cea00ac811c143dd075a52cac7/tools/generate_sql_repositories/main.go#L100

    
    type Configuration struct {
    	EntityName   string
    	DatabaseName string
    	StructFields []tools.StructField
    }
    
    var structNameVarTemplaterFragment = `{{$StructName := print (UppercaseBeginning .DatabaseName) (UppercaseBeginning .EntityName) "Repository" -}}
    {{$EntityName := UppercaseBeginning .EntityName -}}`
    
    var structDefinition = `type {{UppercaseBeginning .DatabaseName}}{{UppercaseBeginning .EntityName}}Repository struct {
        db sql.DB
    }`
    
    var repositoryHelperTypesFragment = structNameVarTemplaterFragment + `
    type {{$EntityName}}Field string
    
    var {{$EntityName}}Fields = struct {
        {{range $field := .StructFields -}}
        {{.FieldName}}  {{$EntityName}}Field
        {{end}}
    }{
        {{range $field := .StructFields -}}
        {{.FieldName}}: "{{.LogicalFieldName -}}",
        {{end}}
    }
    
    var {{$EntityName}}FieldsList = []{{$EntityName}}Field{
        {{range $field := .StructFields -}}
        {{$EntityName}}Field("{{.FieldName}}"),
        {{end}}
    }
    
    type {{$EntityName}}Filter struct {
        {{range $field := .StructFields -}}
        {{.FieldName}} optional.Optional[FilterOperation[{{.FieldType}}]]
        {{end}}
    }
    
    type {{$EntityName}}Updater struct {
        {{range $field := .StructFields -}}
        {{.FieldName}} optional.Optional[UpdateOperation[{{.FieldType}}]]
        {{end}}
    }
    
    type {{$StructName}}Hook func(context.Context, {{$StructName}}) error`
    
    // TODO: Add template for type safe config struct per provider for New() methods
    // It could embed a generic RepositoryConfig into repository specific configurations e.g. Sqlite3RepositoryConfig
    
    func main() {
    	for _, database := range databases {
    		for _, entity := range entities {
    
    

    19d5d01f45bca483c9ac2b13262464436c8e0e7e

  • Allow skipping certain hooks

    Allow skipping certain hooks

    Allow skipping certain hooks

    https://github.com/JonasMuehlmann/bntp.go/blob/e0906c0d1a785b7767a7948b7c898192a058cef6/pkg/libbookmarks/bookmarkmanager.go#L30

    
    
    	"github.com/JonasMuehlmann/bntp.go/domain"
    	bntp "github.com/JonasMuehlmann/bntp.go/pkg"
    )
    
    // TODO: Allow skipping certain hooks
    type BookmarkManager struct {
    	hooks bntp.Hooks[Bookmark]
    }
    
    func (m *BookmarkManager) New(...any) (BookmarkManager, error) {
    }
    
    func (m *BookmarkManager) Add(context.Context, []domain.Bookmark) (numAffectedRecords int, newID int, err error) {
    }
    func (m *BookmarkManager) Replace(context.Context, []domain.Bookmark) error {
    }
    func (m *BookmarkManager) UpdateWhere(context.Context, BookmarkFilter, map[domain.BookmarkField]domain.BookmarkUpdater) (numAffectedRecords int, err error) {
    }
    func (m *BookmarkManager) Delete(context.Context, []domain.Bookmark) error {
    }
    func (m *BookmarkManager) DeleteWhere(context.Context, BookmarkFilter) (numAffectedRecords int, err error) {
    }
    func (m *BookmarkManager) CountWhere(context.Context, BookmarkFilter) int {
    }
    func (m *BookmarkManager) CountAll(context.Context) int {
    }
    func (m *BookmarkManager) DoesExist(context.Context, domain.Bookmark) bool {
    }
    func (m *BookmarkManager) DoesExistWhere(context.Context, BookmarkFilter) bool {
    }
    func (m *BookmarkManager) GetWhere(context.Context, BookmarkFilter) []domain.Bookmark {
    }
    func (m *BookmarkManager) GetFirstWhere(context.Context, BookmarkFilter) domain.Bookmark {
    }
    func (m *BookmarkManager) GetAll(context.Context) []domain.Bookmark {
    }
    
    func (m *BookmarkManager) AddType(context.Context, string) error {
    }
    func (m *BookmarkManager) DeleteType(context.Context, string) error {
    }
    func (m *BookmarkManager) UpdateType(context.Context, string, string) error {
    }
    
    

    dc5ed91d32f9d714107e38fba3aeb89daed28f68

  • Write test to check if all domain types are handled here

    Write test to check if all domain types are handled here

    Write test to check if all domain types are handled here

    https://github.com/JonasMuehlmann/bntp.go/blob/4062c7d12bdf0937444d9e3b99dfa5ff6956c57b/pkg/libbookmarks/bookmarkmanager.go#L92

    
    	DeletedAt
    )
    
    // TODO: Generate from structs
    type BookmarkUpdateOperation func(any) any
    
    // TODO: Generate from structs
    type BookmarkHook func(context.Context, domain.Bookmark) error
    
    // TODO: Add test to compare with constants.
    // TODO: Allow sipping certain hooks
    // TODO: Write test to check if all domain types are handled here
    
    type BookmarkManager struct {
    	Hooks bntp.Hooks[BookmarkHook]
    	Repo  BookmarkRpository
    }
    
    

    83c723228f10bc8295ee054e6e0b7f48c9cab682

  • Allow sipping certain hooks

    Allow sipping certain hooks

    Allow sipping certain hooks

    https://github.com/JonasMuehlmann/bntp.go/blob/4062c7d12bdf0937444d9e3b99dfa5ff6956c57b/pkg/libbookmarks/bookmarkmanager.go#L91

    
    	DeletedAt
    )
    
    // TODO: Generate from structs
    type BookmarkUpdateOperation func(any) any
    
    // TODO: Generate from structs
    type BookmarkHook func(context.Context, domain.Bookmark) error
    
    // TODO: Add test to compare with constants.
    // TODO: Allow sipping certain hooks
    // TODO: Write test to check if all domain types are handled here
    
    type BookmarkManager struct {
    	Hooks bntp.Hooks[BookmarkHook]
    	Repo  BookmarkRpository
    }
    
    

    0b3236e311c6443ab014d36caf0570cca74b0f3d

  • Anki integration

    Anki integration

    type CardFlag string
    
    const (
        CardFlagRed CardFlag = "red"
        CardFlagBlue CardFlag = "blue"
        ...
    )
    
    type CardHighlight struct {
        CardID int64
        Flag CardFlag
        IsSuspended bool
        Note string
    }
    
Go library and CLIs for working with container registries
Go library and CLIs for working with container registries

Go library and CLIs for working with container registries

Dec 27, 2022
Mange your browser bookmarks with CLI.
Mange your browser bookmarks with CLI.

go-bookmark Mange your browser bookmarks with CLI.

Dec 3, 2022
Use the command to convert arbitrary formats to Go Struct (including json, toml, yaml, etc.)

go2struct-tool Use the command to convert arbitrary formats to Go Struct (including json, toml, yaml, etc.) Installation Run the following command und

Dec 16, 2021
go-shellcommand is the package providing the function System like the one of the programming language C.

go-shellcommand go-shellcommand is the package providing the function System like the one of the programming language C. process, err := shellcommand.

Oct 17, 2021
Todos REST API build using echo server.

Todos REST API build using echo server.

Feb 2, 2022
A personal knowledge management and sharing system for VSCode
A personal knowledge management and sharing system for VSCode

Foam ?? This is an early stage project under rapid development. For updates join the Foam community Discord! ?? Foam is a personal knowledge managemen

Jan 9, 2023
OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc
OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc

graph-bars-font OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc. I didn't find anything similar on the net so I decided

Jul 28, 2022
For productivity addicts who enjoys coding while listening to Spotify
For productivity addicts who enjoys coding while listening to Spotify

?? nvim-spotify For productivity addicts who enjoys coding while listening to Sp

Dec 30, 2022
General purpose reloader for all projects.

leaf General purpose reloader for all projects. Command leaf watches for changes in the working directory and runs the specified set of commands whene

Nov 8, 2022
textnote is a command line tool for quickly creating and managing daily plain text notes.

textnote is a command line tool for quickly creating and managing daily plain text notes. It is designed for ease of use to encourage the practice of daily, organized note taking. textnote intentionally facilitates only the management (creation, opening, organizing, and consolidated archiving) of notes, following the philosophy that notes are best written in a text editor and not via a CLI.

Jan 2, 2023
📝 Take notes quickly and expeditiously from terminal
📝 Take notes quickly and expeditiously from terminal

Installation See the last release, where you can find binary files for your ecosystem Curl: curl -sfL https://raw.githubusercontent.com/anonistas/noty

Dec 29, 2022
A very simple note-taking CLI you can use from the terminal that uses a SQLite DB to persist, and query, notes.

Note Logger Summary A very simple note-taking CLI you can use from the terminal that uses a SQLite DB to persist, and query, notes. Building/Installin

Apr 14, 2022
Hosty is a command-line utility that allows for fast inspection and editing of /etc/hosts-like files

Hosty Description Hosty is a command-line utility that allows for fast inspection and editing of /etc/hosts-like files. It is written in golang and us

Sep 3, 2021
Allows you to collect all pprof profiles with one command.

Collect Allows you to collect all pprof profiles with one command. Installation Just go-get it: $ go get github.com/tommsawyer/collect/cmd/collect Mot

Aug 24, 2022
all-in-one cmd tool to search man page of different platform

Overview remote-man is an all-in-one cmd tool to search man page of different platform. support search platform Linux MacOS FreeBSD Installation compi

Oct 31, 2022
A simple CLI app to take notes daily on markdown file
A simple CLI app to take notes daily on markdown file

A simple CLI app to take notes daily on markdown file

Jul 29, 2022
git-glimpse is a command-line tool that is aimed at generating a git prompt like the one from zsh-vcs-prompt.

Git GoGlimpse git-glimpse is a command-line tool that is aimed at generating a git prompt like the one from zsh-vcs-prompt. The particularity of this

Jan 27, 2022
Creating a simple CLI tool in the Go Programming Language for personal learning and fun

Creating a simple CLI tool in the Go Programming Language for personal learning and fun Open to feedback :) Build docker dev environment docker build

Dec 12, 2021
I like reading news but I also like the terminal. I am leaning and practicing my go.
I like reading news but I also like the terminal. I am leaning and practicing my go.

I made an api and didn't know how to use it. Screenshots The initial screen when you first run the app. The screen after you specify an id. This app u

Jan 14, 2022